Compare commits

...

100 Commits

Author SHA1 Message Date
John Preston
6fea6393c6 Beta version 6.2.6: Fix build with GCC. 2025-11-07 17:53:02 +04:00
John Preston
53f063fe16 Beta version 6.2.6: Fix build with Xcode. 2025-11-07 17:10:52 +04:00
John Preston
6ee72b9c32 Beta version 6.2.6.
- Bug fixes in the new profile page design.
2025-11-07 16:39:44 +04:00
John Preston
5faefa7997 Attempt to fix possible crash in quick action. 2025-11-07 16:38:03 +04:00
John Preston
b0726cd31a Don't show lock on sold out gifts. 2025-11-07 16:38:02 +04:00
John Preston
64f5fa8dc3 Revert "Improved focus widgets within chat when foreground box is presented."
This reverts commit 080ecece66.
2025-11-07 16:38:02 +04:00
John Preston
ad300f5eae Customize DividerBar/DividerLabel styles. 2025-11-07 16:38:02 +04:00
John Preston
7331160e22 Improve accessibility changes. 2025-11-07 16:38:02 +04:00
Reza Bakhshi Laktasaraei
6af82e5ae5 Update Telegram/SourceFiles/settings/settings_intro.cpp
Co-authored-by: ilya-fedin <fedin-ilja2010@ya.ru>
2025-11-07 16:10:36 +04:00
Reza Bakhshi Laktasaraei
9b2347171f Update Telegram/SourceFiles/settings/settings_chat.cpp
Co-authored-by: ilya-fedin <fedin-ilja2010@ya.ru>
2025-11-07 16:10:36 +04:00
Reza Bakhshi Laktasaraei
c60629c17d Update Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp
Co-authored-by: ilya-fedin <fedin-ilja2010@ya.ru>
2025-11-07 16:10:36 +04:00
Reza Bakhshi Laktasaraei
353e18e8e3 Update Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp
Co-authored-by: ilya-fedin <fedin-ilja2010@ya.ru>
2025-11-07 16:10:36 +04:00
Reza Bakhshi Laktasaraei
f069df285d Update Telegram/SourceFiles/ui/widgets/continuous_sliders.h
Co-authored-by: ilya-fedin <fedin-ilja2010@ya.ru>
2025-11-07 16:10:36 +04:00
Reza Bakhshi Laktasaraei
6c5f8dffa6 Add accessibility value API to RpWidget
Add keyboard controls and accessibility value reporting to ContinuousSlider
Remove redundant accessibilityRole overrides from intro pages; set Step role to Dialog
2025-11-07 16:10:36 +04:00
Ilya Fedin
4a77e23b54 Skip RpWindow's geometry handling for fullscreen media viewer 2025-11-07 08:51:47 +04:00
23rd
942e53b59b Added option to able revert flexible scroll to first version. 2025-11-06 23:47:03 +03:00
23rd
21a6a256b9 Attempted to fix scroll flickering to profiles. 2025-11-06 23:47:03 +03:00
23rd
99567d3a53 Added second implementation of flexible scroll with event filter. 2025-11-06 23:47:03 +03:00
John Preston
eddad6b690 Show correct price per year in 2year premium.
Fixes #29961.
2025-11-06 22:54:43 +04:00
John Preston
d02a8cbca1 Better handle group conversion and bar settings.
Fixes #29886.
2025-11-06 22:54:43 +04:00
John Preston
3b079aa29a Don't try requesting peer settings in monoforums. 2025-11-06 22:54:42 +04:00
John Preston
df5307dd32 Use "Saved Messages" in chat switcher.
Fixes #29896.
2025-11-06 22:54:42 +04:00
John Preston
65891508e0 Focus start button on launch. 2025-11-06 22:54:42 +04:00
John Preston
b5f4e40c3e Fix build with MSVC. 2025-11-06 22:54:42 +04:00
23rd
a427730acd Replaced static header icons with lottie animations in ShowOrPremiumBox. 2025-11-06 18:24:08 +03:00
23rd
d7639a1ab6 Slightly improved style of last seen button in profile top bar. 2025-11-06 17:34:36 +03:00
23rd
01a140ea29 Fixed crash on erasing peer search result with pressed sponsored button. 2025-11-06 17:27:10 +03:00
23rd
6e0ff9b6a0 Fixed Up key scrolling when no editable messages exist in chat history. 2025-11-06 17:27:10 +03:00
23rd
fb14eeeb1e Replaced full profile with short info on open from paid reactions box. 2025-11-06 17:27:10 +03:00
23rd
0c701d9d95 Added ministar particles to premium bubbles with Credits type. 2025-11-06 17:27:10 +03:00
23rd
f1ceb1c95a Added ministar particles to slider within paid reactions box. 2025-11-06 17:27:10 +03:00
23rd
6bd41a07a1 Added dedicated class for ministar particles effect. 2025-11-06 17:27:10 +03:00
23rd
2b78bb6e79 Added ability to provide width of outline to slider style. 2025-11-06 17:27:10 +03:00
23rd
7e5d5ddafe Added top senders badge only they are present to paid reactions box. 2025-11-06 16:39:37 +03:00
23rd
150c25c81a Added terms of service label and link to paid reaction box. 2025-11-06 16:39:37 +03:00
23rd
95319bdaca Fixed center position of checkbox in paid reactions box. 2025-11-06 16:39:37 +03:00
23rd
d982835b50 Added paid reaction details display on right click on it from message. 2025-11-06 16:39:37 +03:00
23rd
6f040aa0b5 Added empty button for paid reaction to messages with other reactions. 2025-11-06 16:39:37 +03:00
23rd
080ecece66 Improved focus widgets within chat when foreground box is presented. 2025-11-06 16:39:37 +03:00
23rd
31ece2c26e Improved position of video in full screen mode in overlay media viewer. 2025-11-06 16:39:37 +03:00
23rd
9a20f4b935 Fixed display amount of available balance in withdrawal widget. 2025-11-06 16:39:37 +03:00
23rd
ea61fd22f5 Added ability to copy url to second button from withdrawal widget. 2025-11-06 16:39:37 +03:00
23rd
c77f8f9f41 Added external link indicator to second button from withdrawal widget. 2025-11-06 16:39:36 +03:00
23rd
ad758c51c2 Fixed display of url from withdrawal widget when withdrawal disabled. 2025-11-06 16:39:36 +03:00
23rd
8ac7fd14ec Renamed changePhoneIconSize with normalBoxLottieSize. 2025-11-06 16:39:36 +03:00
23rd
ed7c5e97cb Added lottie icon to rtmp box. 2025-11-06 16:39:36 +03:00
23rd
fb928a15a6 Fixed display of unread badges in filters strip after remote reorder. 2025-11-06 16:39:36 +03:00
23rd
f60155e7b8 Slightly improved position of stars rating in profile top bar. 2025-11-06 16:39:36 +03:00
23rd
7c28b1a6a6 Slightly improved pattern color in some cases in profile top bar. 2025-11-06 16:39:36 +03:00
23rd
a300f4662e Fixed width of title with emoji in profile top bar. 2025-11-06 16:39:36 +03:00
23rd
3ff376774f Added emoji selector to name input field in filter edit box. 2025-11-02 18:35:18 +03:00
23rd
e16c05385f Improved length limit for title of filter edit box. 2025-11-02 18:35:18 +03:00
23rd
6e8825cdd5 Added ability to add newly created chat to folder. 2025-11-01 18:50:01 +03:00
23rd
1c19895ce7 Paused emoji in top bar suggestion when window inactive. 2025-11-01 18:50:00 +03:00
23rd
e62a4b065a Extracted common toast timer logic to recent self forwards tagger. 2025-11-01 18:50:00 +03:00
23rd
ba363285a7 Added ability to add newly joined chat to folder. 2025-11-01 18:50:00 +03:00
23rd
679c932697 Added local event for recent joined chats. 2025-11-01 18:50:00 +03:00
23rd
581ec70bf3 Fixed dividers below shared media section in profile info. 2025-11-01 18:50:00 +03:00
23rd
4d2700ab1c Added special tracker to allow override dividers below in profile info. 2025-11-01 18:50:00 +03:00
23rd
b365136639 Added special at least one shown button tracker to profile info. 2025-11-01 18:50:00 +03:00
23rd
f23153e1ac Fixed skip for view channel button in profile info. 2025-11-01 18:50:00 +03:00
23rd
26cb931dcf Fixed skip for report reaction button in profile info. 2025-11-01 18:50:00 +03:00
23rd
42d2190d17 Moved out "add to contact" button from info above in hierarchy. 2025-11-01 18:50:00 +03:00
23rd
63ad80200e Fixed display of join action button in profile top bar on channel join. 2025-11-01 18:50:00 +03:00
23rd
68e9c63693 Slightly improved style of badges in boost box. 2025-11-01 18:50:00 +03:00
23rd
542ff88d3b Fixed order of features in boost box. 2025-11-01 18:50:00 +03:00
23rd
bf7f73e472 Fixed repaint of action buttons in profile top bar on palette changed. 2025-11-01 18:50:00 +03:00
23rd
c9f195be90 Improved color for verified inner check in profile top bar. 2025-11-01 18:50:00 +03:00
23rd
715da30a72 Added peer colors as feature to boost box. 2025-11-01 18:50:00 +03:00
23rd
4e9f2aaadd Removed redundant button for chat management from info actions list. 2025-11-01 18:50:00 +03:00
23rd
71cbb037ce Added new action button for chat management to profile top bar. 2025-11-01 18:50:00 +03:00
23rd
9e43972313 Added item about peer colors to summary of premium features. 2025-10-31 15:42:24 +03:00
23rd
4a8bb75851 Added new badge to entry point for edit peer color box. 2025-10-31 15:04:17 +03:00
23rd
80dce3b65a Added profile color sample to button in settings and manage sections. 2025-10-31 14:41:25 +03:00
23rd
056534fb41 Fixed api requests for edit colors in edit peer color box. 2025-10-31 14:39:06 +03:00
23rd
e444b82683 Fixed display of pattern emoji in button without color in edit color. 2025-10-31 12:12:00 +03:00
23rd
d9e4f686fb Added menu items for contacts to profile top bar. 2025-10-31 11:57:30 +03:00
23rd
2cd224af98 Fixed centered position of icons in action buttons from profile top bar. 2025-10-31 10:26:35 +03:00
23rd
a50141ac8a Added one line for private topic links to profile info. 2025-10-31 01:21:14 +03:00
23rd
f11b36cdb1 Improved track of sliders to avoid empty dividers in profile top bar. 2025-10-31 01:20:55 +03:00
23rd
f30af1c4ed Added go to forum button instead of status for topic in profile top bar. 2025-10-30 22:25:06 +03:00
23rd
45def31826 Returned button to view channel to profile top bar.
Regression was introduced 6147b0eec0.
2025-10-30 21:28:34 +03:00
23rd
a22afce820 Added music button to stories section. 2025-10-30 21:15:59 +03:00
23rd
af874bebfd Moved out music button creation to single place. 2025-10-30 21:15:59 +03:00
23rd
7cdb651538 Slightly optimized repaint for userpic and gifts in profile top bar. 2025-10-30 20:43:07 +03:00
23rd
5e1752bcbc Fixed useless repaint for bot verified badges. 2025-10-30 20:31:54 +03:00
23rd
eb295cb19c Simplified foreground color of badges in profile top bar. 2025-10-30 20:31:54 +03:00
23rd
ace42226b6 Fix rounding in preview of settings for quick dialog actions. 2025-10-30 18:49:41 +03:00
23rd
5e1b4b4e6a Added ability to report userpic from profile top bar. 2025-10-30 18:05:27 +03:00
23rd
f009fa9e47 Fixed callback of back button in profile top bar. 2025-10-30 18:05:27 +03:00
23rd
f08c00557d Fixed display of back button in profile top bar. 2025-10-30 18:05:27 +03:00
23rd
a0e0d95775 Added ability to click on emoji status in profile top bar. 2025-10-30 18:05:05 +03:00
23rd
a4acab983d Fixed width of title with bot verified badge in profile top bar. 2025-10-30 18:05:05 +03:00
23rd
5237a7977d Fixed ability to select and copy peer name in profile top bar. 2025-10-30 18:05:05 +03:00
23rd
eb285bc1ac Fixed width of title on peer name changes in profile top bar. 2025-10-30 18:05:05 +03:00
23rd
7d97cd25ab Fixed bg position of pinned gift in profile top bar on non-retina. 2025-10-30 14:54:52 +03:00
23rd
55f4a99824 Fixed quality of pinned gift in profile top bar on non-retina. 2025-10-30 14:02:31 +03:00
23rd
34373836b9 Fixed position of pattern emoji in profile top bar on different scales. 2025-10-30 14:02:31 +03:00
23rd
1509891ec0 Fixed size of pattern emoji in profile top bar on non-retina. 2025-10-30 14:02:31 +03:00
dependabot[bot]
877ef7d78f Bump actions/upload-artifact from 4 to 5
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-29 17:41:11 +04:00
149 changed files with 2808 additions and 689 deletions

View File

@@ -148,7 +148,7 @@ jobs:
cd out/Debug
mkdir artifact
mv {Telegram,Updater} artifact/
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v5
if: env.UPLOAD_ARTIFACT == 'true'
name: Upload artifact.
with:

View File

@@ -131,7 +131,7 @@ jobs:
mkdir artifact
mv Telegram.app artifact/
mv Updater artifact/
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v5
if: env.UPLOAD_ARTIFACT == 'true'
name: Upload artifact.
with:

View File

@@ -187,7 +187,7 @@ jobs:
cd $REPO_NAME/build
mkdir artifact
mv Telegram.dmg artifact/
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v5
if: env.UPLOAD_ARTIFACT == 'true'
name: Upload artifact.
with:

View File

@@ -84,7 +84,7 @@ jobs:
mkdir artifact
mv $artifact_name artifact
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v5
if: env.UPLOAD_ARTIFACT == 'true'
name: Upload artifact.
with:

View File

@@ -203,7 +203,7 @@ jobs:
mkdir artifact
move %OUT%\Telegram.exe artifact/
move %OUT%\Updater.exe artifact/
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v5
name: Upload artifact.
if: (env.UPLOAD_ARTIFACT == 'true') || (github.ref == 'refs/heads/nightly')
with:

View File

@@ -1072,6 +1072,7 @@ PRIVATE
info/reactions_list/info_reactions_list_widget.h
info/requests_list/info_requests_list_widget.cpp
info/requests_list/info_requests_list_widget.h
info/saved/info_saved_music_common.cpp
info/saved/info_saved_music_common.h
info/saved/info_saved_music_provider.cpp
info/saved/info_saved_music_provider.h

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 764 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Icon / Filled / premium_themes</title>
<g id="Icon-/-Filled-/-premium_themes" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M8.65381112,10.7516786 L14.600547,16.5507975 C14.7950866,16.740508 14.7950866,17.0480895 14.600547,17.2378 L13.8655201,17.9545811 C13.6709805,18.1442916 13.3555695,18.1442916 13.1610299,17.9545811 C12.1129217,17.0843554 11.3456336,16.4119807 10.8591655,15.937457 C10.7682049,15.8487297 10.5422415,15.7847321 10.4567355,15.904017 L10.201902,16.2497487 L10.0193453,16.4884401 L9.83300263,16.7270352 L9.02218186,17.7460428 L8.75587192,18.0918034 L8.59549123,18.3121046 C7.91167738,19.2768629 6.91083947,19.1001271 6.35158932,18.5547593 C5.80232578,18.0191302 5.85863289,17.1049913 6.79138874,16.4035319 L6.85891507,16.3573404 C6.99427087,16.2669684 7.14265185,16.157786 7.30037755,16.0361677 L7.68363141,15.7328223 L8.38962036,15.1596258 L8.6312678,14.9665056 C8.79873654,14.8340204 8.96615336,14.7048192 9.13062469,14.5839136 C9.24335825,14.5010414 9.22165977,14.3680769 9.1319646,14.280627 L8.89532907,14.0428177 C8.55768805,13.6947027 8.13153904,13.2224438 7.61688206,12.6260412 L7.21429408,12.1554622 C7.01975448,11.9657517 7.01975448,11.6581702 7.21429408,11.4684597 L7.9493209,10.7516786 C8.1438605,10.5619681 8.45927152,10.5619681 8.65381112,10.7516786 Z M14.0750341,4.02554926 C14.1700659,4.05313428 14.2562711,4.10500261 14.3244531,4.17672529 L17.4193876,7.36611999 C17.5229412,7.47283414 17.5525486,7.63117292 17.4945519,7.76809496 L16.3521671,10.465106 C16.3075918,10.570342 16.3567671,10.6917881 16.4620031,10.7363634 C16.5226789,10.7620641 16.5919675,10.7573313 16.648585,10.7236188 L18.8014275,9.44172252 C18.9580015,9.3484915 19.1591462,9.38314731 19.2754925,9.52340059 L19.824657,10.1854078 C20.080453,10.493765 20.0450803,10.9495406 19.7447707,11.2147384 L15.2419116,15.19113 L10.0408804,10.1008505 C10.8928945,9.23732408 11.6272327,8.25187058 12.2438948,7.14448999 C12.7106069,6.30638457 13.08566,5.39611751 13.3690539,4.41368881 C13.456971,4.1116676 13.7729463,3.93786178 14.0750341,4.02554926 Z" id="Shape" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -742,6 +742,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_language" = "Language";
"lng_settings_default_scale" = "Default interface scale";
"lng_settings_scale" = "Interface scale";
"lng_settings_connection_type" = "Connection type";
"lng_settings_downloading_update" = "Downloading update {progress}...";
"lng_settings_privacy_title" = "Privacy";
@@ -1631,6 +1632,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_delete_note" = "Delete Note";
"lng_info_bio_label" = "Bio";
"lng_info_link_label" = "Link";
"lng_info_link_topic_label" = "This topic link will only work for group members";
"lng_info_location_label" = "Location";
"lng_info_about_label" = "Description";
"lng_info_work_open" = "Open";
@@ -1693,6 +1695,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_profile_action_short_report" = "Report";
"lng_profile_action_short_leave" = "Leave";
"lng_profile_action_short_more" = "More";
"lng_profile_action_short_manage" = "Manage";
"lng_media_type_photos" = "Photos";
"lng_media_type_gifs" = "GIFs";
@@ -3379,6 +3382,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_feature_custom_emoji_pack" = "Custom Emoji Pack";
"lng_feature_transcribe" = "Voice-to-Text Conversion";
"lng_feature_autotranslate" = "Autotranslation of Messages";
"lng_feature_profile_color_channel#one" = "**{count}** Color for Channel Cover";
"lng_feature_profile_color_channel#other" = "**{count}** Colors for Channel Cover";
"lng_feature_profile_color_group#one" = "**{count}** Color for Group Cover";
"lng_feature_profile_color_group#other" = "**{count}** Colors for Group Cover";
"lng_feature_profile_icon_channel" = "Custom Logo for Channel Cover";
"lng_feature_profile_icon_group" = "Custom Logo for Group Cover";
"lng_edit_topics_enable" = "Enable Topics";
"lng_edit_topics_about" = "The group chat will be divided into topics created by admins or users.";
@@ -4572,6 +4581,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_message_tagged_with" = "Message tagged with {emoji}";
"lng_tagged_view_saved" = "View";
"lng_add_channel_to_filter_selector" = "You can add a channel to your folder";
"lng_add_group_to_filter_selector" = "You can add a group to your folder";
"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**.";

View File

@@ -46,6 +46,10 @@
<file alias="toast/saved_messages.tgs">../../animations/toast/saved_messages.tgs</file>
<file alias="toast/tagged.tgs">../../animations/toast/tagged.tgs</file>
<file alias="my_gifts_empty.tgs">../../animations/my_gifts_empty.tgs</file>
<file alias="toast/chats_filter_in.tgs">../../animations/toast/chats_filter_in.tgs</file>
<file alias="rtmp.tgs">../../animations/rtmp.tgs</file>
<file alias="show_or_premium_lastseen.tgs">../../animations/show_or_premium_lastseen.tgs</file>
<file alias="show_or_premium_readtime.tgs">../../animations/show_or_premium_readtime.tgs</file>
<file alias="profile_muting.tgs">../../animations/profile/profile_muting.tgs</file>
<file alias="profile_unmuting.tgs">../../animations/profile/profile_unmuting.tgs</file>

View File

@@ -10,7 +10,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="6.2.5.0" />
Version="6.2.6.0" />
<Properties>
<DisplayName>Telegram Desktop</DisplayName>
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>

View File

@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 6,2,5,0
PRODUCTVERSION 6,2,5,0
FILEVERSION 6,2,6,0
PRODUCTVERSION 6,2,6,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -62,10 +62,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop"
VALUE "FileVersion", "6.2.5.0"
VALUE "FileVersion", "6.2.6.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2025"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "6.2.5.0"
VALUE "ProductVersion", "6.2.6.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 6,2,5,0
PRODUCTVERSION 6,2,5,0
FILEVERSION 6,2,6,0
PRODUCTVERSION 6,2,6,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -53,10 +53,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop Updater"
VALUE "FileVersion", "6.2.5.0"
VALUE "FileVersion", "6.2.6.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2025"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "6.2.5.0"
VALUE "ProductVersion", "6.2.6.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -36,7 +36,9 @@ Data::PremiumSubscriptionOption CreateSubscriptionOption(
.costNoDiscount = Ui::FillAmountAndCurrency(
monthlyAmount * months,
currency),
.costTotal = Ui::FillAmountAndCurrency(amount, currency),
.costPerYear = Ui::FillAmountAndCurrency(
amount / float64(months / 12.),
currency),
.botUrl = botUrl,
};
}

View File

@@ -1216,6 +1216,10 @@ void ApiWrap::gotUserFull(
void ApiWrap::requestPeerSettings(not_null<PeerData*> peer) {
if (!_requestedPeerSettings.emplace(peer).second) {
return;
} else if (peer->isMonoforum()) {
peer->setBarSettings(PeerBarSettings());
_requestedPeerSettings.erase(peer);
return;
}
request(MTPmessages_GetPeerSettings(
peer->input
@@ -1227,6 +1231,7 @@ void ApiWrap::requestPeerSettings(not_null<PeerData*> peer) {
_requestedPeerSettings.erase(peer);
});
}).fail([=] {
peer->setBarSettings(PeerBarSettings());
_requestedPeerSettings.erase(peer);
}).send();
}
@@ -1740,6 +1745,11 @@ void ApiWrap::joinChannel(not_null<ChannelData*> channel) {
)).done([=](const MTPUpdates &result) {
_channelAmInRequests.remove(channel);
applyUpdates(result);
session().data().addRecentJoinChat({
.fromPeerId = channel->id,
.joinedPeerId = channel->id,
});
}).fail([=](const MTP::Error &error) {
const auto &type = error.type();

View File

@@ -111,6 +111,10 @@ void ChatCreateDone(
show,
chat,
CollectForbiddenUsers(&chat->session(), result));
chat->owner().addRecentJoinChat({
.fromPeerId = chat->id,
.joinedPeerId = chat->id,
});
}
};
if (!success) {

View File

@@ -537,7 +537,6 @@ changePhoneDescription: FlatLabel(defaultFlatLabel) {
}
changePhoneDescriptionPadding: margins(0px, 1px, 0px, 8px);
changePhoneIconPadding: margins(0px, 39px, 0px, 5px);
changePhoneIconSize: 120px;
changePhoneLabel: FlatLabel(defaultFlatLabel) {
minWidth: 275px;
textFg: windowSubTextFg;
@@ -546,6 +545,8 @@ changePhoneError: FlatLabel(changePhoneLabel) {
textFg: boxTextFgError;
}
normalBoxLottieSize: size(120px, 120px);
adminLogFilterUserpicLeft: 15px;
adminLogFilterLittleSkip: 16px;
adminLogFilterCheckbox: Checkbox(defaultBoxCheckbox) {

View File

@@ -16,11 +16,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/premium_preview_box.h"
#include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/message_field.h"
#include "chat_helpers/tabbed_panel.h"
#include "chat_helpers/tabbed_selector.h"
#include "core/application.h"
#include "core/core_settings.h"
#include "core/ui_integration.h"
#include "data/stickers/data_custom_emoji.h"
#include "data/stickers/data_stickers.h"
#include "data/data_channel.h"
#include "data/data_chat_filters.h"
#include "data/data_document.h"
#include "data/data_peer.h"
#include "data/data_peer_values.h" // Data::AmPremiumValue.
#include "data/data_premium_limits.h"
@@ -32,6 +37,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "settings/settings_common.h"
#include "ui/chat/chats_filter_tag.h"
#include "ui/controls/emoji_button_factory.h"
#include "ui/controls/emoji_button.h"
#include "ui/effects/animation_value_f.h"
#include "ui/effects/animations.h"
#include "ui/effects/panel_animation.h"
@@ -54,6 +61,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_layers.h"
#include "styles/style_window.h"
#include "styles/style_chat.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_info_userpic_builder.h"
namespace {
@@ -357,6 +365,7 @@ void EditFilterBox(
rpl::variable<TextWithEntities> title;
rpl::variable<bool> staticTitle;
rpl::variable<int> colorIndex;
base::unique_qptr<ChatHelpers::TabbedPanel> emojiPanel;
};
const auto owner = &window->session().data();
const auto state = box->lifetime().make_state<State>(State{
@@ -423,7 +432,22 @@ void EditFilterBox(
current.text,
TextUtilities::ConvertEntitiesToTextTags(current.entities),
}, Ui::InputField::HistoryAction::Clear);
name->setMaxLength(kMaxFilterTitleLength);
Ui::AddLengthLimitLabel(
name,
kMaxFilterTitleLength,
Ui::LengthLimitLabelOptions{
.customThreshold = 0,
.customUpdatePosition = [=](QSize parent, QSize label) {
return QPoint(
parent.width()
- st::windowFilterNameCharsLimitRightPosition.x()
- label.width() / 2,
st::windowFilterNameCharsLimitRightPosition.y());
},
.customCharactersCount = [=] {
return Ui::ComputeFieldCharacterCount(name);
},
});
const auto nameEditing = box->lifetime().make_state<NameEditing>(
NameEditing{ name });
@@ -468,6 +492,47 @@ void EditFilterBox(
nameEditing->custom = true;
}, box->lifetime());
using Selector = ChatHelpers::TabbedSelector;
state->emojiPanel = base::make_unique_q<ChatHelpers::TabbedPanel>(
box->getDelegate()->outerContainer(),
window,
object_ptr<Selector>(
nullptr,
window->uiShow(),
Window::GifPauseReason::Layer,
Selector::Mode::EmojiOnly));
state->emojiPanel->setDesiredHeightValues(
1.,
st::emojiPanMinHeight / 2,
st::emojiPanMinHeight);
state->emojiPanel->hide();
state->emojiPanel->selector()->setCurrentPeer(window->session().user());
state->emojiPanel->selector()->emojiChosen(
) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) {
Ui::InsertEmojiAtCursor(name->textCursor(), data.emoji);
}, name->lifetime());
state->emojiPanel->selector()->customEmojiChosen(
) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
const auto info = data.document->sticker();
if (info
&& info->setType == Data::StickersType::Emoji
&& !window->session().premium()) {
ShowPremiumPreviewBox(
window,
PremiumFeature::AnimatedEmoji);
} else {
Data::InsertCustomEmoji(name, data.document);
}
}, name->lifetime());
const auto emojiButton = Ui::AddEmojiToggleToField(
name,
box,
window,
state->emojiPanel.get(),
st::windowFilterNameEmojiPosition);
emojiButton->show();
name->changes(
) | rpl::start_with_next([=] {
if (!nameEditing->settingDefault) {
@@ -741,7 +806,8 @@ void EditFilterBox(
const auto staticTitle = !title.entities.isEmpty()
&& state->staticTitle.current();
const auto rules = data->current();
if (title.empty()) {
if (Ui::ComputeFieldCharacterCount(name) > kMaxFilterTitleLength
|| title.empty()) {
name->showError();
box->scrollToY(0);
return {};

View File

@@ -466,8 +466,7 @@ void CreateModerateMessagesBox(
inner->add(object_ptr<Ui::DividerLabel>(
inner,
std::move(label),
st::defaultBoxDividerLabelPadding,
RectPart::Top | RectPart::Bottom));
st::defaultBoxDividerLabelPadding));
using Flag = ChatRestriction;
using Flags = ChatRestrictions;

View File

@@ -1070,6 +1070,10 @@ void AddParticipantsBoxController::Start(
channel,
params,
ShowAtTheEndMsgId);
channel->owner().addRecentJoinChat({
.fromPeerId = channel->id,
.joinedPeerId = channel->id,
});
}
}, box->lifetime());
}

View File

@@ -56,6 +56,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/path_shift_gradient.h"
#include "ui/effects/premium_graphics.h"
#include "ui/layers/generic_box.h"
#include "ui/new_badges.h"
#include "ui/peer/color_sample.h"
#include "ui/text/text_utilities.h"
#include "ui/widgets/buttons.h"
@@ -95,10 +96,7 @@ base::unique_qptr<Ui::RpWidget> CreateEmptyPlaceholder(
container,
{
.name = u"my_gifts_empty"_q,
.sizeOverride = {
st::changePhoneIconSize,
st::changePhoneIconSize,
},
.sizeOverride = st::normalBoxLottieSize,
},
st::settingsBlockedListIconPadding);
const auto iconWidget = icon.widget.data();
@@ -581,15 +579,17 @@ void Set(
using Flag = MTPaccount_UpdateColor::Flag;
using ColorFlag = MTPDpeerColor::Flag;
send(MTPaccount_UpdateColor(
MTP_flags(Flag::f_color
| (values.forProfile ? Flag::f_for_profile : Flag(0))),
MTP_flags((values.forProfile ? Flag::f_for_profile : Flag(0))
| (values.colorIndex != kUnsetColorIndex
? Flag::f_color
: Flag(0))),
(values.colorCollectible
? MTP_inputPeerColorCollectible(
MTP_long(values.colorCollectible->collectibleId))
: MTP_peerColor(
MTP_flags(ColorFlag()
| ColorFlag::f_color
| (values.backgroundEmojiId || !values.forProfile
| (values.backgroundEmojiId
? ColorFlag::f_background_emoji_id
: ColorFlag(0))),
MTP_int(values.colorIndex),
@@ -598,7 +598,9 @@ void Set(
if (peer->isBroadcast()) {
using Flag = MTPchannels_UpdateColor::Flag;
send(MTPchannels_UpdateColor(
MTP_flags(Flag::f_color
MTP_flags((values.colorIndex != kUnsetColorIndex
? Flag::f_color
: Flag(0))
| Flag::f_background_emoji_id
| (values.forProfile ? Flag::f_for_profile : Flag(0))),
channel->inputChannel,
@@ -760,7 +762,7 @@ void Apply(
}, right->lifetime());
const auto session = &show->session();
const auto added = st::normalFont->spacew;
const auto added = st::lineWidth * 2;
std::move(emojiIdValue) | rpl::start_with_next([=](DocumentId emojiId) {
state->emojiId = emojiId;
state->emoji = emojiId
@@ -790,13 +792,16 @@ void Apply(
}
auto p = QPainter(right);
const auto height = right->height();
if (state->emoji && state->index != kUnsetColorIndex) {
if (state->emoji
&& (state->index != kUnsetColorIndex || profileIndices)) {
const auto profileSet = profileIndices
? peer->session().api().peerColors().colorProfileFor(
state->index)
: std::nullopt;
const auto textColor = profileSet && !profileSet->palette.empty()
? profileSet->palette.front()
: profileIndices
? style->windowActiveTextFg()->c
: style->coloredValues(false, state->index).name;
state->emoji->paint(p, {
.textColor = textColor,
@@ -1157,7 +1162,7 @@ Fn<void()> AddColorGiftTabs(
}
container->resizeToWidth(container->width());
}, container->lifetime());
return [=]() {
const auto &list = state->list.current();
if (!list.empty()) {
@@ -2529,8 +2534,16 @@ void SetupPeerColorSample(
) | rpl::map([=] {
return peer->colorCollectible();
});
auto colorProfileIndexValue = peer->session().changes().peerFlagsValue(
peer,
Data::PeerUpdate::Flag::ColorProfile
) | rpl::map([=] {
return peer->colorProfileIndex();
});
const auto name = peer->shortName();
const auto sampleSize = st::settingsColorSampleSize;
const auto sample = Ui::CreateChild<Ui::ColorSample>(
button.get(),
[=] { return Core::TextContext({ .session = &peer->session() }); },
@@ -2541,21 +2554,39 @@ void SetupPeerColorSample(
name);
sample->show();
const auto profileSample = Ui::CreateChild<Ui::ColorSample>(
button.get(),
[=, peerColors = &peer->session().api().peerColors()](uint8 index) {
return peerColors->colorProfileFor(peer).value_or(
Data::ColorProfileSet{});
},
0,
false);
profileSample->hide();
profileSample->resize(sampleSize, sampleSize);
rpl::combine(
button->widthValue(),
rpl::duplicate(label),
rpl::duplicate(colorIndexValue)
rpl::duplicate(colorIndexValue),
rpl::duplicate(colorProfileIndexValue)
) | rpl::start_with_next([=](
int width,
const QString &button,
int colorIndex) {
const auto sampleSize = st::settingsColorSampleSize;
const QString &buttonText,
int colorIndex,
std::optional<uint8> profileIndex) {
const auto available = width
- st::settingsButton.padding.left()
- (st::settingsColorButton.padding.right() - sampleSize)
- st::settingsButton.style.font->width(button)
- st::settingsButton.style.font->width(buttonText)
- st::settingsButtonRightSkip;
if (style->colorPatternIndex(colorIndex)) {
const auto hasProfile = profileIndex.has_value();
profileSample->setVisible(hasProfile);
sample->setForceCircle(hasProfile);
if (style->colorPatternIndex(colorIndex) || hasProfile) {
sample->resize(sampleSize, sampleSize);
} else {
const auto padding = st::settingsColorSamplePadding;
@@ -2566,13 +2597,24 @@ void SetupPeerColorSample(
sample->resize(std::min(wantedWidth, available), wantedHeight);
}
sample->update();
sample->setCutoutPadding(hasProfile
? st::settingsColorSampleCutout
: 0);
profileSample->update();
}, sample->lifetime());
rpl::combine(
button->sizeValue(),
sample->sizeValue(),
std::move(colorIndexValue)
) | rpl::start_with_next([=](QSize outer, QSize inner, int colorIndex) {
rpl::duplicate(colorIndexValue),
rpl::duplicate(colorProfileIndexValue)
) | rpl::start_with_next([=](
QSize outer,
QSize inner,
int colorIndex,
std::optional<uint8> profileIndex) {
const auto hasColor = (colorIndex != 0);
const auto right = st::settingsColorButton.padding.right()
- st::settingsColorSampleSkip
- st::settingsColorSampleSize
@@ -2582,9 +2624,18 @@ void SetupPeerColorSample(
sample->move(
outer.width() - right - inner.width(),
(outer.height() - inner.height()) / 2);
profileSample->move(
sample->pos().x()
+ (hasColor
? (st::settingsColorProfileSampleShift
- st::settingsColorSampleSize
- st::lineWidth)
: 0),
sample->pos().y());
}, sample->lifetime());
sample->setAttribute(Qt::WA_TransparentForMouseEvents);
profileSample->setAttribute(Qt::WA_TransparentForMouseEvents);
}
void AddPeerColorButton(
@@ -2611,6 +2662,32 @@ void AddPeerColorButton(
SetupPeerColorSample(button, peer, rpl::duplicate(label), style);
}
{
const auto badge = Ui::NewBadge::CreateNewBadge(
button,
tr::lng_premium_summary_new_badge()).get();
rpl::combine(
rpl::duplicate(label),
button->widthValue()
) | rpl::start_with_next([=](
const QString &text,
int width) {
const auto space = st.style.font->spacew;
const auto left = st.padding.left()
+ st.style.font->width(text)
+ space;
const auto available = width - left - st.padding.right();
badge->setVisible(available >= badge->width());
if (!badge->isHidden()) {
const auto top = st.padding.top()
+ st.style.font->ascent
- st::settingsPremiumNewBadge.style.font->ascent
- st::settingsPremiumNewBadgePadding.top();
badge->moveToLeft(left, top, width);
}
}, badge->lifetime());
}
button->setClickedCallback([=] {
show->show(Box(EditPeerColorBox, show, peer, style, theme));
});

View File

@@ -433,6 +433,7 @@ Ui::BoostCounters ParseBoostCounters(
Ui::BoostFeatures LookupBoostFeatures(not_null<ChannelData*> channel) {
auto nameColorsByLevel = base::flat_map<int, int>();
auto linkStylesByLevel = base::flat_map<int, int>();
auto profileColorsByLevel = base::flat_map<int, int>();
const auto group = channel->isMegagroup();
const auto peerColors = &channel->session().api().peerColors();
const auto &list = group
@@ -445,6 +446,29 @@ Ui::BoostFeatures LookupBoostFeatures(not_null<ChannelData*> channel) {
}
++linkStylesByLevel[level];
}
{
const auto profileIndices = peerColors->profileColorIndices();
auto lowestNonZeroLevel = std::numeric_limits<int>::max();
auto levels = std::vector<int>();
levels.reserve(profileIndices.size());
for (const auto index : profileIndices) {
const auto level = peerColors->requiredLevelFor(
channel->id,
index,
group,
true);
levels.push_back(level);
if (level) {
lowestNonZeroLevel = std::min(lowestNonZeroLevel, level);
}
}
for (const auto level : levels) {
++profileColorsByLevel[std::max(level, lowestNonZeroLevel)];
}
}
const auto &themes = channel->owner().cloudThemes().chatThemes();
if (themes.empty()) {
channel->owner().cloudThemes().refreshChatThemes();
@@ -453,7 +477,11 @@ Ui::BoostFeatures LookupBoostFeatures(not_null<ChannelData*> channel) {
return Ui::BoostFeatures{
.nameColorsByLevel = std::move(nameColorsByLevel),
.linkStylesByLevel = std::move(linkStylesByLevel),
.profileColorsByLevel = std::move(profileColorsByLevel),
.linkLogoLevel = group ? 0 : levelLimits.channelBgIconLevelMin(),
.profileIconLevel = group
? levelLimits.groupProfileBgIconLevelMin()
: levelLimits.channelProfileBgIconLevelMin(),
.autotranslateLevel = group ? 0 : levelLimits.channelAutoTranslateLevelMin(),
.transcribeLevel = group ? levelLimits.groupTranscribeLevelMin() : 0,
.emojiPackLevel = group ? levelLimits.groupEmojiStickersLevelMin() : 0,

View File

@@ -147,8 +147,7 @@ void ShowReportMessageBox(
auto label = object_ptr<Ui::FlatLabel>(
container,
tr::lng_report_details_message_about(),
st::boxDividerLabel);
label->setTextColorOverride(st->dividerFg->c);
st->divider.label);
using namespace Ui;
const auto widget = container->add(
object_ptr<PaddingWrap<>>(
@@ -159,7 +158,7 @@ void ShowReportMessageBox(
= CreateChild<BoxContentDivider>(
widget,
st::boxDividerHeight,
st->dividerBg,
st->divider.bar,
RectPart::Top | RectPart::Bottom);
background->lower();
widget->sizeValue(

View File

@@ -2439,6 +2439,30 @@ void SendGiftBox(
const auto premiumNeeded = star && star->info.requirePremium;
if (premiumNeeded && !peer->session().premium()) {
Settings::ShowPremiumGiftPremium(window, star->info);
} else if (unique && star->resale) {
window->show(Box(
Settings::GlobalStarGiftBox,
window->uiShow(),
star->info,
Settings::StarGiftResaleInfo{
.recipientId = peer->id,
.forceTon = star->forceTon,
},
Settings::CreditsEntryBoxStyleOverrides()));
} else if (star && star->resale) {
const auto id = star->info.id;
if (state->resaleRequestingId == id) {
return;
}
state->resaleRequestingId = id;
state->resaleLifetime = ShowStarGiftResale(
window,
peer,
id,
star->info.resellTitle,
[=] { state->resaleRequestingId = 0; });
} else if (star && IsSoldOut(star->info)) {
window->show(Box(SoldOutBox, window, *star));
} else if (star
&& star->info.lockedUntilDate
&& star->info.lockedUntilDate > base::unixtime::now()) {
@@ -2495,30 +2519,6 @@ void SendGiftBox(
Api::InputSavedStarGiftId(savedId, unique),
peer->input),
formReady);
} else if (unique && star->resale) {
window->show(Box(
Settings::GlobalStarGiftBox,
window->uiShow(),
star->info,
Settings::StarGiftResaleInfo{
.recipientId = peer->id,
.forceTon = star->forceTon,
},
Settings::CreditsEntryBoxStyleOverrides()));
} else if (star && star->resale) {
const auto id = star->info.id;
if (state->resaleRequestingId == id) {
return;
}
state->resaleRequestingId = id;
state->resaleLifetime = ShowStarGiftResale(
window,
peer,
id,
star->info.resellTitle,
[=] { state->resaleRequestingId = 0; });
} else if (star && IsSoldOut(star->info)) {
window->show(Box(SoldOutBox, window, *star));
} else if (star
&& star->info.perUserTotal
&& !star->info.perUserRemains) {

View File

@@ -624,11 +624,12 @@ groupCallMenuCover: ShortInfoCover(shortInfoCover) {
namePosition: point(17px, 28px);
statusPosition: point(17px, 8px);
}
groupCallTextPalette: TextPalette(defaultTextPalette) {
linkFg: groupCallActiveFg;
}
groupCallMenuAbout: FlatLabel(defaultFlatLabel) {
textFg: groupCallMemberNotJoinedStatus;
palette: TextPalette(defaultTextPalette) {
linkFg: groupCallActiveFg;
}
palette: groupCallTextPalette;
minWidth: 200px;
maxHeight: 92px;
}
@@ -1082,6 +1083,19 @@ groupCallBox: Box(defaultBox) {
groupCallLayerBox: Box(groupCallBox) {
buttonPadding: margins(8px, 8px, 8px, 8px);
}
groupCallDividerBar: DividerBar {
bg: groupCallBg;
top: icon {{ "box_divider_top", groupCallMemberNotJoinedStatus }};
bottom: icon {{ "box_divider_bottom", groupCallMemberNotJoinedStatus }};
}
groupCallDividerLabel: DividerLabel {
bar: groupCallDividerBar;
label: FlatLabel(boxDividerLabel) {
textFg: groupCallVideoSubTextFg;
palette: groupCallTextPalette;
}
}
groupCallLevelMeter: LevelMeter(defaultLevelMeter) {
height: 18px;
lineWidth: 3px;

View File

@@ -13,8 +13,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_chat.h"
#include "data/data_user.h"
#include "lang/lang_keys.h"
#include "lottie/lottie_icon.h"
#include "main/main_account.h"
#include "main/main_session.h"
#include "settings/settings_common.h"
#include "ui/boxes/confirm_box.h"
#include "ui/layers/generic_box.h"
#include "ui/text/text_utilities.h"
@@ -48,6 +50,21 @@ void StartWithBox(
};
const auto state = box->lifetime().make_state<State>();
{
auto icon = Settings::CreateLottieIcon(
box->verticalLayout(),
{
.name = u"rtmp"_q,
.sizeOverride = st::normalBoxLottieSize,
},
{});
box->verticalLayout()->add(std::move(icon.widget), {}, style::al_top);
box->setShowFinishedCallback([animate = icon.animate] {
animate(anim::repeat::loop);
});
Ui::AddSkip(box->verticalLayout());
}
StartRtmpProcess::FillRtmpRows(
box->verticalLayout(),
true,

View File

@@ -285,7 +285,7 @@ void SettingsBox(
layout->add(object_ptr<Ui::BoxContentDivider>(
layout,
st::boxDividerHeight,
st::groupCallDividerBg));
st::groupCallDividerBar));
};
if (addCheck || addMessages) {

View File

@@ -213,7 +213,10 @@ Switcher::Switcher(
not_null<Ui::RpWidget*> parent,
rpl::producer<bool> &&toggled)
: RpWidget(parent)
, _background(this, st::groupCallRecordingInfoHeight, st::groupCallBg)
, _background(
this,
st::groupCallRecordingInfoHeight,
st::groupCallDividerBar)
, _audio(this)
, _video(this) {
_audio->prepareAudio();

View File

@@ -225,8 +225,7 @@ ReportBox {
noIconButton: SettingsButton;
label: FlatLabel;
field: InputField;
dividerBg: color;
dividerFg: color;
divider: DividerLabel;
spam: icon;
fake: icon;
violence: icon;
@@ -824,6 +823,29 @@ selfForwardsTaggerToast: Toast(defaultToast) {
padding: margins(54px, 12px, 19px, 12px);
iconPosition: point(15px, 6px);
}
joinChatAddToFilterToast: Toast(defaultToast) {
minWidth: 160px;
maxWidth: 380px;
radius: 9px;
padding: margins(54px, 12px, 44px, 12px);
iconPosition: point(15px, 6px);
}
joinChatAddToFilterToastButton: IconButton(defaultIconButton) {
width: 40px;
height: 40px;
icon: icon{
{ "chat/reactions_round_small", windowBgRipple },
{ "chat/reactions_expand_panel", windowSubTextFg },
};
iconOver: icon{
{ "chat/reactions_round_small", windowBgRipple },
{ "chat/reactions_expand_panel", windowSubTextFg },
};
rippleAreaPosition: point(10px, 10px);
rippleAreaSize: 20px;
ripple: universalRippleAnimation;
}
choosePeerGroupIcon: icon {{ "info/edit/create_group", lightButtonFg }};
choosePeerChannelIcon: icon {{ "info/edit/create_channel", lightButtonFg }};
@@ -1447,8 +1469,7 @@ defaultReportBox: ReportBox {
}
label: boxLabel;
field: newGroupDescription;
dividerBg: boxDividerBg;
dividerFg: windowSubTextFg;
divider: defaultDividerLabel;
spam: menuIconDelete;
fake: menuIconFake;
violence: menuIconViolence;

View File

@@ -1426,10 +1426,7 @@ void FrozenInfoBox(
content,
{
.name = u"media_forbidden"_q,
.sizeOverride = {
st::changePhoneIconSize,
st::changePhoneIconSize,
},
.sizeOverride = st::normalBoxLottieSize,
},
st::settingLocalPasscodeIconPadding);
content->add(std::move(icon.widget));

View File

@@ -68,6 +68,7 @@ enum class StickerLottieSize : uint8 {
EmojiInteractionReserved7,
ChatIntroHelloSticker,
StickerEmojiSize,
PinnedProfileUniqueGiftSize,
};
[[nodiscard]] uint8 LottieCacheKeyShift(
uint8 replacementsTag,

View File

@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
constexpr auto AppName = "Telegram Desktop"_cs;
constexpr auto AppFile = "Telegram"_cs;
constexpr auto AppVersion = 6002005;
constexpr auto AppVersionStr = "6.2.5";
constexpr auto AppVersion = 6002006;
constexpr auto AppVersionStr = "6.2.6";
constexpr auto AppBetaVersion = true;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

View File

@@ -952,6 +952,13 @@ void PeerData::setBarSettings(PeerBarSettings which) {
history->refreshHiddenLinksItems();
});
}
if (const auto from = migrateFrom()) {
if (const auto history = owner().historyLoaded(from)) {
crl::on_main(&history->session(), [=] {
history->refreshHiddenLinksItems();
});
}
}
}
}
@@ -980,6 +987,9 @@ bool PeerData::hideLinks() const {
//if (!isUser()) {
// return false;
//}
if (const auto to = migrateTo()) {
return to->hideLinks();
}
const auto settings = barSettings();
return !settings || (*settings & PeerBarSetting::ReportSpam);
}

View File

@@ -15,7 +15,7 @@ struct PremiumSubscriptionOption {
QString discount;
QString costPerMonth;
QString costNoDiscount;
QString costTotal;
QString costPerYear;
QString currency;
QString total;
QString botUrl;

View File

@@ -5299,6 +5299,14 @@ rpl::producer<RecentSelfForwards> Session::recentSelfForwards() const {
return _recentSelfForwards.events();
}
void Session::addRecentJoinChat(const RecentJoinChat &data) {
_recentJoinChat.fire_copy(data);
}
rpl::producer<RecentJoinChat> Session::recentJoinChat() const {
return _recentJoinChat.events();
}
void Session::clearLocalStorage() {
_cache->close();
_cache->clear();

View File

@@ -126,6 +126,11 @@ struct RecentSelfForwards {
MessageIdsList ids;
};
struct RecentJoinChat {
PeerId fromPeerId = 0;
PeerId joinedPeerId = 0;
};
class Session final {
public:
using ViewElement = HistoryView::Element;
@@ -895,6 +900,9 @@ public:
void addRecentSelfForwards(const RecentSelfForwards &data);
[[nodiscard]] rpl::producer<RecentSelfForwards> recentSelfForwards() const;
void addRecentJoinChat(const RecentJoinChat &data);
[[nodiscard]] rpl::producer<RecentJoinChat> recentJoinChat() const;
void clearLocalStorage();
private:
@@ -1264,6 +1272,7 @@ private:
NextToUpgradeGift> _nextForUpgradeGifts;
rpl::event_stream<RecentSelfForwards> _recentSelfForwards;
rpl::event_stream<RecentJoinChat> _recentJoinChat;
rpl::lifetime _lifetime;

View File

@@ -2113,6 +2113,7 @@ bool InnerWidget::addQuickActionRipple(
}
auto name = ResolveQuickDialogLottieIconName(type);
const auto rowHeight = row->height();
context->icon = Lottie::MakeIcon({
.name = std::move(name),
.sizeOverride = Size(st::dialogsQuickActionSize),
@@ -2121,16 +2122,12 @@ bool InnerWidget::addQuickActionRipple(
context->icon->jumpTo(context->icon->framesCount() - 1, [=] {
const auto size = QSize(
st::dialogsQuickActionRippleSize,
row->height());
const auto isRemovingFromList
= (action == Dialogs::Ui::QuickDialogAction::Archive);
rowHeight);
if (!context->ripple) {
context->ripple = std::make_unique<Ui::RippleAnimation>(
st::defaultRippleAnimation,
Ui::RippleAnimation::RectMask(size),
isRemovingFromList
? Fn<void()>([=] { update(); })
: updateCallback);
updateCallback);
}
if (!context->rippleFg) {
context->rippleFg = std::make_unique<Ui::RippleAnimation>(
@@ -2147,13 +2144,11 @@ bool InnerWidget::addQuickActionRipple(
Rect(size),
context->icon.get(),
ResolveQuickDialogLabel(
row->history(),
history,
action,
_filterId));
}),
isRemovingFromList
? Fn<void()>([=] { update(); })
: std::move(updateCallback));
std::move(updateCallback));
}
context->ripple->add(QPoint(size.width() / 2, size.height() / 2));
context->rippleFg->add(QPoint(size.width() / 2, size.height() / 2));
@@ -3273,6 +3268,18 @@ void InnerWidget::showSponsoredMenu(int peerSearchIndex, QPoint globalPos) {
const auto peer = entry->peer;
const auto remove = crl::guard(this, [=] {
_sponsoredRemoved.emplace(peer);
if (_pressedRightButtonData) {
for (const auto &result : _peerSearchResults) {
if (result->peer == peer
&& result->sponsored
&& _pressedRightButtonData
== &result->sponsored->button) {
_pressedRightButtonData = nullptr;
_pressedRightButton = false;
break;
}
}
}
_peerSearchResults.erase(
ranges::remove(
_peerSearchResults,
@@ -3601,12 +3608,17 @@ void InnerWidget::clearSearchResults(bool alsoPeerSearchResults) {
}
void InnerWidget::clearPeerSearchResults() {
_peerSearchResults.clear();
if (_pressedRightButtonSponsored) {
_pressedRightButtonData = nullptr;
_pressedRightButtonSponsored = false;
_pressedRightButton = false;
if (_pressedRightButtonData) {
for (const auto &result : _peerSearchResults) {
if (result->sponsored
&& _pressedRightButtonData == &result->sponsored->button) {
_pressedRightButtonData = nullptr;
_pressedRightButton = false;
break;
}
}
}
_peerSearchResults.clear();
}
void InnerWidget::clearPreviewResults() {
@@ -4169,7 +4181,7 @@ void InnerWidget::refreshEmpty() {
_emptyList,
{
.name = u"no_chats"_q,
.sizeOverride = Size(st::changePhoneIconSize),
.sizeOverride = st::normalBoxLottieSize,
});
_emptyList->add(std::move(icon.widget), style::al_top);
Ui::AddSkip(_emptyList);

View File

@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_authorization.h"
#include "lang/lang_keys.h"
#include "ui/rect.h"
#include "ui/power_saving.h"
#include "ui/text/format_values.h"
#include "ui/text/text_custom_emoji.h"
#include "ui/ui_rpl_filter.h"
@@ -236,6 +237,7 @@ void TopBarSuggestionContent::draw(QPainter &p) {
? availableWidth
: (availableWidth - titleRight),
.availableWidth = availableWidth,
.pausedEmoji = On(PowerSaving::kEmojiChat),
.elisionLines = hasSecondLineTitle ? 2 : 1,
});
}
@@ -276,6 +278,7 @@ void TopBarSuggestionContent::draw(QPainter &p) {
.geometry = Ui::Text::GeometryDescriptor{
.layout = std::move(lineLayout),
},
.pausedEmoji = On(PowerSaving::kEmojiChat),
});
_lastPaintedContentTop = top;
_lastPaintedContentLineAmount = lastContentLineAmount;

View File

@@ -2351,20 +2351,31 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
: 0;
const auto session = &this->session();
_whoReactedMenuLifetime.destroy();
if (!clickedReaction.empty()
&& leaderOrSelf
&& Api::WhoReactedExists(leaderOrSelf, Api::WhoReactedList::One)) {
HistoryView::ShowWhoReactedMenu(
&_menu,
e->globalPos(),
this,
leaderOrSelf,
clickedReaction,
_controller,
_whoReactedMenuLifetime);
e->accept();
return;
} else if (!linkPhoneNumber.isEmpty()) {
if (!clickedReaction.empty() && leaderOrSelf) {
if (clickedReaction.paid()) {
Payments::ShowPaidReactionDetails(
_controller,
leaderOrSelf,
viewByItem(leaderOrSelf),
HistoryReactionSource::Selector);
e->accept();
return;
} else if (Api::WhoReactedExists(
leaderOrSelf,
Api::WhoReactedList::One)) {
HistoryView::ShowWhoReactedMenu(
&_menu,
e->globalPos(),
this,
leaderOrSelf,
clickedReaction,
_controller,
_whoReactedMenuLifetime);
e->accept();
return;
}
}
if (!linkPhoneNumber.isEmpty()) {
PhoneClickHandler(session, linkPhoneNumber).onClick(
prepareClickContext(
Qt::LeftButton,

View File

@@ -7629,7 +7629,8 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) {
: nullptr;
if (item) {
editMessage(item, {});
return;
} else {
_scroll->keyPressEvent(e);
}
}
} else if (e->key() == Qt::Key_Up

View File

@@ -801,7 +801,7 @@ void InsufficientTonBox(
box->verticalLayout(),
{
.name = u"diamond"_q,
.sizeOverride = Size(st::changePhoneIconSize),
.sizeOverride = st::normalBoxLottieSize,
},
{});
box->setShowFinishedCallback([animate = std::move(icon.animate)] {

View File

@@ -10,8 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/call_delayed.h"
#include "base/event_filter.h"
#include "base/timer_rpl.h"
#include "boxes/choose_filter_box.h"
#include "chat_helpers/share_message_phrase_factory.h"
#include "core/ui_integration.h"
#include "data/data_chat_filters.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "data/stickers/data_custom_emoji.h"
@@ -27,10 +29,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/toast/toast_widget.h"
#include "ui/toast/toast.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/popup_menu.h"
#include "ui/widgets/tooltip.h"
#include "window/window_session_controller.h"
#include "styles/style_chat.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_info.h"
namespace HistoryView {
namespace {
@@ -65,6 +69,20 @@ void SelfForwardsTagger::setup() {
}
showSelectorForMessages(data.ids);
}, _lifetime);
_controller->session().data().recentJoinChat(
) | rpl::start_with_next([=](const Data::RecentJoinChat &data) {
if (!_controller->session().data().chatsFilters().has()) {
return;
}
const auto history = _history ? _history() : nullptr;
if (!history || history->peer->id != data.fromPeerId) {
return;
}
const auto peerId = data.joinedPeerId;
if (const auto peer = _controller->session().data().peer(peerId)) {
showChannelFilterToast(peer);
}
}, _lifetime);
}
void SelfForwardsTagger::showSelectorForMessages(
@@ -170,35 +188,13 @@ void SelfForwardsTagger::showSelectorForMessages(
base::install_event_filter(selector, list, eventFilterCallback);
}
struct State {
rpl::lifetime timerLifetime;
bool expanded = false;
};
const auto state = selector->lifetime().make_state<State>();
const auto restartTimer = [=](crl::time ms) {
state->timerLifetime.destroy();
base::timer_once(ms) | rpl::start_with_next([=] {
hideAndDestroy();
}, state->timerLifetime);
};
const auto state = selector->lifetime().make_state<ToastTimerState>();
selector->willExpand() | rpl::start_with_next([=] {
state->expanded = true;
}, selector->lifetime());
base::install_event_filter(selector, [=](not_null<QEvent*> event) {
if (event->type() == QEvent::MouseButtonPress) {
state->timerLifetime.destroy();
return base::EventFilterResult::Continue;
} else if (!state->expanded && event->type() == QEvent::Enter) {
state->timerLifetime.destroy();
return base::EventFilterResult::Continue;
} else if (!state->expanded && event->type() == QEvent::Leave) {
restartTimer(kTimerOnLeave);
return base::EventFilterResult::Continue;
}
return base::EventFilterResult::Continue;
}, selector->lifetime());
setupToastTimer(selector, state, hideAndDestroy);
QObject::connect(
_toast->widget(),
@@ -219,7 +215,6 @@ void SelfForwardsTagger::showSelectorForMessages(
rect.x() + (rect.width() - selector->width()) / 2,
rect::bottom(rect) - st::selfForwardsTaggerStripSkip);
}, selector->lifetime());
restartTimer(kInitTimer);
selector->show();
}
@@ -336,6 +331,95 @@ void SelfForwardsTagger::showTaggedToast(DocumentId reaction) {
}
}
void SelfForwardsTagger::showChannelFilterToast(not_null<PeerData*> peer) {
hideToast();
const auto toastText = peer->isChannel() && !peer->isMegagroup()
? tr::lng_add_channel_to_filter_selector(tr::now)
: tr::lng_add_group_to_filter_selector(tr::now);
_toast = Ui::Toast::Show(_scroll, Ui::Toast::Config{
.text = { .text = toastText },
.st = &st::joinChatAddToFilterToast,
.attach = RectPart::Top,
.acceptinput = true,
.infinite = true,
});
if (const auto strong = _toast.get()) {
const auto widget = strong->widget();
createLottieIcon(widget, u"toast/chats_filter_in"_q);
const auto rightButton = createRightButton(widget);
const auto history = peer->owner().history(peer);
const auto state = widget->lifetime().make_state<ToastTimerState>();
rightButton->setClickedCallback([=] {
state->expanded = true;
state->timerLifetime.destroy();
const auto menu = Ui::CreateChild<Ui::PopupMenu>(
rightButton,
st::foldersMenu);
menu->setForcedOrigin(Ui::PanelAnimation::Origin::TopRight);
FillChooseFilterMenu(_controller, menu, history);
if (!menu->empty()) {
menu->popup(
rightButton->mapToGlobal(
QPoint(
rightButton->width(),
rightButton->height() + rightButton->y())));
QObject::connect(menu, &QObject::destroyed, [=] {
hideToast();
});
} else {
hideToast();
}
});
setupToastTimer(widget, state, [=] { hideToast(); });
}
}
not_null<Ui::AbstractButton*> SelfForwardsTagger::createRightButton(
not_null<Ui::RpWidget*> widget) {
const auto button = Ui::CreateChild<Ui::IconButton>(
widget.get(),
st::joinChatAddToFilterToastButton);
widget->sizeValue() | rpl::start_with_next([=](const QSize &size) {
button->moveToRight(
st::lineWidth * 4,
(size.height() - button->height()) / 2);
}, button->lifetime());
button->show();
return button;
}
void SelfForwardsTagger::setupToastTimer(
not_null<Ui::RpWidget*> widget,
not_null<ToastTimerState*> state,
Fn<void()> hideCallback) {
const auto restartTimer = [=](crl::time ms) {
state->timerLifetime.destroy();
base::timer_once(ms) | rpl::start_with_next([=] {
hideCallback();
}, state->timerLifetime);
};
base::install_event_filter(widget, [=](not_null<QEvent*> event) {
if (event->type() == QEvent::MouseButtonPress) {
state->timerLifetime.destroy();
return base::EventFilterResult::Continue;
} else if (!state->expanded && event->type() == QEvent::Enter) {
state->timerLifetime.destroy();
return base::EventFilterResult::Continue;
} else if (!state->expanded && event->type() == QEvent::Leave) {
restartTimer(kTimerOnLeave);
return base::EventFilterResult::Continue;
}
return base::EventFilterResult::Continue;
}, state->timerLifetime);
restartTimer(kInitTimer);
}
void SelfForwardsTagger::hideToast() {
if (const auto strong = _toast.get()) {
strong->hideAnimated();

View File

@@ -29,6 +29,7 @@ class SessionController;
namespace Ui {
class RpWidget;
class AbstractButton;
} // namespace Ui
namespace Ui::Toast {
@@ -53,11 +54,23 @@ public:
~SelfForwardsTagger();
private:
struct ToastTimerState {
rpl::lifetime timerLifetime;
bool expanded = false;
};
void setup();
void showSelectorForMessages(const MessageIdsList &ids);
void showToast(const TextWithEntities &text, Fn<void()> callback);
void showTaggedToast(DocumentId);
void showChannelFilterToast(not_null<PeerData*> peer);
void createLottieIcon(not_null<QWidget*> widget, const QString &name);
not_null<Ui::AbstractButton*> createRightButton(
not_null<Ui::RpWidget*> widget);
void setupToastTimer(
not_null<Ui::RpWidget*> widget,
not_null<ToastTimerState*> state,
Fn<void()> hideCallback);
void hideToast();
[[nodiscard]] QRect toastGeometry() const;

View File

@@ -14,6 +14,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_group_call_bar.h"
#include "core/click_handler_types.h"
#include "data/stickers/data_custom_emoji.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_message_reactions.h"
#include "data/data_peer.h"
#include "data/data_user.h"
@@ -24,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/chat/chat_style.h"
#include "ui/effects/reaction_fly_animation.h"
#include "ui/painter.h"
#include "ui/rect.h"
#include "ui/power_saving.h"
#include "styles/style_chat.h"
#include "styles/style_chat_helpers.h"
@@ -225,8 +228,13 @@ void InlineList::setButtonCount(Button &button, int count) {
button.userpics = nullptr;
button.count = count;
button.tag = false;
button.text = Lang::FormatCountToShort(count).string;
button.textWidth = st::semiboldFont->width(button.text);
if (count == 0) {
button.text = QString();
button.textWidth = 0;
} else {
button.text = Lang::FormatCountToShort(count).string;
button.textWidth = st::semiboldFont->width(button.text);
}
}
void InlineList::setButtonUserpics(
@@ -305,6 +313,8 @@ QSize InlineList::countOptimalSize() {
+ (button.textWidth ? st::reactionInlineSkip : 0))
: button.userpics
? (widthBaseUserpics + userpicsWidth(button))
: button.count == 0
? (rect::m::sum::h(padding) + size - st::reactionInlineEmptySkip)
: (widthBaseCount + button.textWidth);
button.geometry.setSize({ width, height });
x += width + between;
@@ -828,6 +838,33 @@ InlineListData InlineListDataFromMessage(not_null<Element*> view) {
const auto item = view->data();
auto result = InlineListData();
result.reactions = item->reactionsWithLocal();
const auto shouldAddEmptyPaidButton = [&] {
if (view->context() == Context::ChatPreview) {
return false;
}
if (result.reactions.empty()) {
return false;
}
const auto hasPaidReaction = ranges::any_of(
result.reactions,
[](const MessageReaction &r) { return r.id.paid(); });
if (hasPaidReaction) {
return false;
}
if (const auto channel = item->history()->peer->asChannel()) {
return channel->allowedReactions().paidEnabled;
} else if (const auto chat = item->history()->peer->asChat()) {
return chat->allowedReactions().paidEnabled;
}
return false;
}();
if (shouldAddEmptyPaidButton) {
result.reactions.insert(
result.reactions.begin(),
MessageReaction{ .id = ReactionId::Paid(), .count = 0 });
}
if (const auto user = item->history()->peer->asUser()) {
// Always show userpics, we have all information.
result.recent.reserve(result.reactions.size());

View File

@@ -247,14 +247,11 @@ void InnerWidget::fill() {
return _state.buyAdsUrl;
})
),
peer()->isSelf()
? rpl::duplicate(overallBalanceValue) | rpl::type_erased()
: rpl::duplicate(availableBalanceValue),
rpl::duplicate(availableBalanceValue),
rpl::duplicate(dateValue),
_state.isWithdrawalEnabled,
(peer()->isSelf()
? rpl::duplicate(overallBalanceValue) | rpl::type_erased()
: rpl::duplicate(availableBalanceValue)
rpl::duplicate(
availableBalanceValue
) | rpl::map([=](CreditsAmount v) {
return v ? ToUsd(v, multiplier, kMinorLength) : QString();
}));

View File

@@ -213,7 +213,7 @@ void AddPremiumTopBarWithDefaultTitleBar(
- st::boxTitleHeight
+ st::boxDividerHeight
+ st::defaultVerticalListSkip,
st::boxDividerBg,
st::defaultDividerBar,
RectPart::Bottom),
style::margins());
bar->setPaused(true);

View File

@@ -358,7 +358,7 @@ void InnerWidget::fill() {
container,
Dialogs::SearchEmptyIcon::NoResults,
tr::lng_search_tab_no_results(Ui::Text::Bold)));
empty->setMinimalHeight(st::changePhoneIconSize);
empty->setMinimalHeight(st::normalBoxLottieSize.height());
empty->animate();
return;
}
@@ -607,8 +607,7 @@ void InnerWidget::fill() {
container->add(object_ptr<Ui::DividerLabel>(
container,
std::move(label),
st::defaultBoxDividerLabelPadding,
RectPart::Top | RectPart::Bottom));
st::defaultBoxDividerLabelPadding));
};
if (canViewCurrencyEarn) {
addAboutWithLearn(bot

View File

@@ -413,7 +413,7 @@ infoProfileTopBarPhotoTop: 24px;
infoProfileTopBarPhotoBgShift: 55px;
infoProfileTopBarPhotoBgNoActionsShift: 30px;
infoProfileTopBarPhotoSize: 80px;
infoProfileTopBarLastSeenSkip: 8px;
infoProfileTopBarLastSeenSkip: point(8px, 1px);
infoProfileTopBarGiftSize: 20px;
@@ -426,8 +426,8 @@ infoProfileTopBarGiftBottomRight: point(40px, 13px);
infoProfileTopBarActionButtonBgOpacity: 0.16;
infoProfileTopBarActionButtonSize: 52px;
infoProfileTopBarActionButtonIconSize: 24px;
infoProfileTopBarActionButtonLottieSize: infoProfileTopBarActionButtonIconSize - 2px;
infoProfileTopBarActionButtonIconSize: 23px;
infoProfileTopBarActionButtonLottieSize: infoProfileTopBarActionButtonIconSize - 0px;
infoProfileTopBarActionButtonIconTop: 6px;
infoProfileTopBarActionButtonTextTop: infoProfileTopBarActionButtonIconSize + infoProfileTopBarActionButtonIconTop + 1px;
infoProfileTopBarActionButtonTextSkip: 4px;
@@ -444,15 +444,20 @@ infoProfileTopBarNoActionsHeightMax: infoProfileTopBarHeightMax - infoProfileTop
infoProfileTopBarActionMenuSkip: 10px;
infoProfileTopBarActionMessage: icon{{ "profile/message-22x22", windowBoldFg }};
infoProfileTopBarActionMute: icon{{ "profile/mute-22x22", windowBoldFg }};
infoProfileTopBarActionUnmute: icon{{ "profile/unmute-22x22", windowBoldFg }};
infoProfileTopBarActionCall: icon{{ "profile/call-22x22", windowBoldFg }};
infoProfileTopBarActionGift: icon{{ "profile/gift-22x22", windowBoldFg }};
infoProfileTopBarActionJoin: icon{{ "profile/join-22x22", windowBoldFg }};
infoProfileTopBarActionReport: icon{{ "profile/report-22x22", windowBoldFg }};
infoProfileTopBarActionLeave: icon{{ "profile/leave-22x22", windowBoldFg }};
infoProfileTopBarActionMessage: icon{{ "profile/message-23x23", windowBoldFg }};
infoProfileTopBarActionMute: icon{{ "profile/mute-23x23", windowBoldFg }};
infoProfileTopBarActionUnmute: icon{{ "profile/unmute-23x23", windowBoldFg }};
infoProfileTopBarActionCall: icon{{ "profile/call-23x23", windowBoldFg }};
infoProfileTopBarActionGift: icon{{ "profile/gift-23x23", windowBoldFg }};
infoProfileTopBarActionJoin: icon{{ "profile/join-23x23", windowBoldFg }};
infoProfileTopBarActionReport: icon{{ "profile/report-23x23", windowBoldFg }};
infoProfileTopBarActionLeave: icon{{ "profile/leave-23x23", windowBoldFg }};
infoProfileTopBarActionMore: icon{{ "profile/profile_more", windowBoldFg }};
infoProfileTopBarActionManage: icon{{ "profile/profile_manage", windowBoldFg }};
infoProfileTopBarTopicStatusButton: RoundButton(defaultTableSmallButton) {
radius: 4px;
}
infoProfileTopBarBoostFooter: FlatLabel(defaultFlatLabel) {
textFg: windowSubTextFg;
@@ -472,6 +477,20 @@ infoProfileTopBarBoostFooterColored: FlatLabel(infoProfileTopBarBoostFooter) {
}
}
infoProfileTopBarShowLastSeen: RoundButton(defaultActiveButton) {
textFg: windowBoldFg;
textFgOver: windowBoldFg;
textBg: windowBg;
textBgOver: windowBgOver;
width: -12px;
height: 18px;
textTop: 0px;
style: TextStyle(defaultTextStyle) {
font: font(12px);
}
ripple: universalRippleAnimation;
}
infoMinimalWidth: 324px;
infoDesiredWidth: 392px;
infoMinimalLayerMargin: 48px;
@@ -591,7 +610,7 @@ infoColoredPeerBadge: InfoPeerBadge(infoPeerBadge) {
verified: icon {{ "profile_verified_star", groupCallMembersFg }}; // Will be colorized.
verifiedCheck: icon {{ "profile_verified_check", groupCallMembersFg }};
premium: icon {{ "profile_premium", groupCallMembersFg }};
premiumFg: groupCallMembersFg;
premiumFg: groupCallVideoSubTextFg;
}
infoColoredBotVerifyBadge: InfoPeerBadge(infoColoredPeerBadge) {
position: point(-2px, 2px);

View File

@@ -8,6 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/info_flexible_scroll.h"
#include "ui/widgets/scroll_area.h"
#include "base/event_filter.h"
#include "base/options.h"
#include "styles/style_info.h"
#include <QtWidgets/QApplication>
@@ -15,6 +17,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Info {
base::options::toggle AlternativeScrollProcessing({
.id = kAlternativeScrollProcessing,
.name = "Use legacy scroll processing in profiles.",
});
const char kAlternativeScrollProcessing[] = "alternative-scroll-processing";
FlexibleScrollHelper::FlexibleScrollHelper(
not_null<Ui::ScrollArea*> scroll,
not_null<Ui::RpWidget*> inner,
@@ -29,7 +38,11 @@ FlexibleScrollHelper::FlexibleScrollHelper(
, _setViewport(setViewport)
, _data(data) {
setupScrollAnimation();
setupScrollHandling();
if (AlternativeScrollProcessing.value()) {
setupScrollHandling();
} else {
setupScrollHandlingWithFilter();
}
}
void FlexibleScrollHelper::setupScrollAnimation() {
@@ -195,4 +208,135 @@ void FlexibleScrollHelper::setupScrollHandling() {
}));
}
void FlexibleScrollHelper::setupScrollHandlingWithFilter() {
const auto heightDiff = [=] {
return _pinnedToTop->maximumHeight()
- _pinnedToTop->minimumHeight();
};
rpl::combine(
_pinnedToTop->heightValue(),
_inner->heightValue()
) | rpl::start_with_next([=](int, int h) {
_data.contentHeightValue.fire(h + heightDiff());
}, _pinnedToTop->lifetime());
const auto singleStep = _scroll->verticalScrollBar()->singleStep()
* QApplication::wheelScrollLines();
const auto step1 = (_pinnedToTop->maximumHeight()
< st::infoProfileTopBarHeightMax)
? (st::infoProfileTopBarStep2 + st::lineWidth)
: st::infoProfileTopBarStep1;
const auto step2 = st::infoProfileTopBarStep2;
base::install_event_filter(_scroll->verticalScrollBar(), [=](
not_null<QEvent*> e) {
if (e->type() != QEvent::Wheel) {
return base::EventFilterResult::Continue;
}
const auto wheel = static_cast<QWheelEvent*>(e.get());
const auto delta = wheel->angleDelta().y();
if (std::abs(delta) != 120) {
return base::EventFilterResult::Continue;
}
const auto actualTop = _scroll->scrollTop();
const auto animationActive = _scrollAnimation.animating()
&& (_lastScrollApplied != _scrollTopTo);
const auto top = animationActive
? (_lastScrollApplied ? _lastScrollApplied : actualTop)
: actualTop;
const auto diff = (delta > 0) ? -singleStep : singleStep;
const auto previousValue = top;
const auto targetTop = top + diff;
const auto nextStep = (diff > 0)
? ((previousValue == 0)
? step1
: (previousValue == step1)
? step2
: -1)
: ((targetTop < step1)
? 0
: (targetTop < step2)
? step1
: -1);
if (animationActive
&& ((_scrollTopTo > _scrollTopFrom) != (diff > 0))) {
auto overriddenDirection = true;
if (_scrollTopTo > _scrollTopFrom) {
if (_scrollTopTo == step1) {
_scrollTopTo = 0;
} else if (_scrollTopTo == step2) {
_scrollTopTo = step1;
} else {
overriddenDirection = false;
}
} else {
if (_scrollTopTo == 0) {
_scrollTopTo = step1;
} else if (_scrollTopTo == step1) {
_scrollTopTo = step2;
} else {
overriddenDirection = false;
}
}
if (overriddenDirection) {
_timeOffset = crl::now() - _scrollAnimation.started();
_scrollTopFrom = _lastScrollApplied
? _lastScrollApplied
: top;
return base::EventFilterResult::Cancel;
} else {
_scrollAnimation.stop();
_scrollTopFrom = 0;
_scrollTopTo = 0;
_timeOffset = 0;
_lastScrollApplied = 0;
}
}
_scrollTopFrom = top;
if (!animationActive) {
_scrollTopTo = (nextStep != -1) ? nextStep : targetTop;
_scrollAnimation.start();
} else {
if (_scrollTopTo > _scrollTopFrom) {
if (_scrollTopTo == step1) {
_scrollTopTo = step2;
} else {
_scrollTopTo += diff;
}
} else {
if (_scrollTopTo == step2) {
_scrollTopTo = step1;
} else if (_scrollTopTo == step1) {
_scrollTopTo = 0;
} else {
_scrollTopTo += diff;
}
}
_timeOffset = crl::now() - _scrollAnimation.started();
}
return base::EventFilterResult::Cancel;
}, _filterLifetime);
_scroll->scrollTopValue(
) | rpl::start_with_next([=](int top) {
const auto current = heightDiff() - top;
_inner->moveToLeft(0, std::min(0, current));
_pinnedToTop->resize(
_pinnedToTop->width(),
std::max(current + _pinnedToTop->minimumHeight(), 0));
}, _inner->lifetime());
_data.fillerWidthValue.events(
) | rpl::start_with_next([=](int w) {
_inner->resizeToWidth(w);
}, _inner->lifetime());
_setPaintPadding({ 0, _pinnedToTop->minimumHeight(), 0, 0 });
_setViewport(_pinnedToTop->events(
) | rpl::filter([](not_null<QEvent*> e) {
return e->type() == QEvent::Wheel;
}));
}
} // namespace Info

View File

@@ -13,6 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Info {
extern const char kAlternativeScrollProcessing[];
struct FlexibleScrollData {
rpl::event_stream<int> contentHeightValue;
rpl::event_stream<int> fillerWidthValue;
@@ -31,6 +33,7 @@ public:
private:
void setupScrollAnimation();
void setupScrollHandling();
void setupScrollHandlingWithFilter();
const not_null<Ui::ScrollArea*> _scroll;
const not_null<Ui::RpWidget*> _inner;
@@ -46,6 +49,7 @@ private:
int _lastScrollApplied = 0;
int _scrollTopPrevious = 0;
bool _applyingFakeScrollState = false;
rpl::lifetime _filterLifetime;
};
} // namespace Info

View File

@@ -298,7 +298,7 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor, Mode mode) {
_stars->setColorOverride(
Ui::Premium::CreditsIconGradientStops());
}
_lockedUntilDate = data.info.lockedUntilDate;
_lockedUntilDate = data.resale ? 0 : data.info.lockedUntilDate;
});
refreshLocked();
@@ -632,16 +632,20 @@ void GiftButton::cacheUniqueBackground(
void GiftButton::paintEvent(QPaintEvent *e) {
auto p = QPainter(this);
const auto unique = v::is<GiftTypeStars>(_descriptor)
? v::get<GiftTypeStars>(_descriptor).info.unique.get()
: nullptr;
const auto stargift = std::get_if<GiftTypeStars>(&_descriptor);
const auto unique = stargift ? stargift->info.unique.get() : nullptr;
const auto onsale = unique && unique->starsForResale && small();
const auto requirePremium = v::is<GiftTypeStars>(_descriptor)
&& !v::get<GiftTypeStars>(_descriptor).userpic
&& !v::get<GiftTypeStars>(_descriptor).info.unique
&& v::get<GiftTypeStars>(_descriptor).info.requirePremium;
const auto hidden = v::is<GiftTypeStars>(_descriptor)
&& v::get<GiftTypeStars>(_descriptor).hidden;
const auto requirePremium = stargift
&& !stargift->userpic
&& !stargift->info.unique
&& stargift->info.requirePremium;
const auto hidden = stargift && stargift->hidden;
const auto soldOut = stargift
&& !(stargift->pinned || stargift->pinnedSelection)
&& !unique
&& !stargift->userpic
&& stargift->info.limitedCount
&& !stargift->info.limitedLeft;
const auto extend = currentExtend();
const auto position = QPoint(extend.left(), extend.top());
const auto background = _delegate->background();
@@ -683,7 +687,7 @@ void GiftButton::paintEvent(QPaintEvent *e) {
progress * (thickness * 2 + st::giftBoxUserpicSkip)));
}
}
if (_locked) {
if (_locked && !soldOut) {
st::giftBoxLockIcon.paint(
p,
position + st::giftBoxLockIconPosition,
@@ -780,10 +784,6 @@ void GiftButton::paintEvent(QPaintEvent *e) {
const auto count = data.info.limitedCount;
const auto pinned = data.pinned || data.pinnedSelection;
if (count || pinned) {
const auto soldOut = !pinned
&& !unique
&& !data.userpic
&& !data.info.limitedLeft;
const auto yourLeft = data.info.perUserTotal
? (data.info.perUserRemains
? tr::lng_gift_stars_your_left(

View File

@@ -967,14 +967,19 @@ template <typename Text, typename ToggleOn, typename Callback>
ToggleOn &&toggleOn,
Callback &&callback,
Ui::MultiSlideTracker &tracker,
Ui::MultiSlideTracker *buttonTracker,
const style::SettingsButton &st = st::infoMainButton) {
tracker.track(AddActionButton(
const auto button = AddActionButton(
parent,
std::move(text) | Ui::Text::ToUpper(),
std::move(toggleOn),
std::move(callback),
nullptr,
st));
st);
tracker.track(button);
if (buttonTracker) {
buttonTracker->track(button);
}
}
rpl::producer<CreditsAmount> AddCurrencyAction(
@@ -1147,15 +1152,21 @@ public:
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<PeerData*> peer,
Origin origin);
Origin origin,
Ui::MultiSlideTracker &mainTracker,
rpl::variable<bool> &dividerOverridden);
DetailsFiller(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<Data::SavedSublist*> sublist);
not_null<Data::SavedSublist*> sublist,
Ui::MultiSlideTracker &mainTracker,
rpl::variable<bool> &dividerOverridden);
DetailsFiller(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<Data::ForumTopic*> topic);
not_null<Data::ForumTopic*> topic,
Ui::MultiSlideTracker &mainTracker,
rpl::variable<bool> &dividerOverridden);
object_ptr<Ui::RpWidget> fill();
@@ -1164,12 +1175,19 @@ private:
object_ptr<Ui::RpWidget> setupInfo();
void setupMainApp();
void setupBotPermissions();
void addViewChannelButton(
Ui::MultiSlideTracker &tracker,
not_null<ChannelData*> channel,
Ui::MultiSlideTracker *buttonTracker);
void addReportReaction(Ui::MultiSlideTracker &tracker);
void addReportReaction(
Ui::MultiSlideTracker &tracker,
Ui::MultiSlideTracker *buttonTracker);
void addReportReaction(
GroupReactionOrigin data,
bool ban,
Ui::MultiSlideTracker &tracker);
Ui::MultiSlideTracker &tracker,
Ui::MultiSlideTracker *buttonTracker);
template <
typename Widget,
@@ -1189,6 +1207,8 @@ private:
Data::ForumTopic *_topic = nullptr;
Data::SavedSublist *_sublist = nullptr;
Origin _origin;
Ui::MultiSlideTracker &_mainTracker;
rpl::variable<bool> &_dividerOverridden;
object_ptr<Ui::VerticalLayout> _wrap;
};
@@ -1279,33 +1299,45 @@ DetailsFiller::DetailsFiller(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<PeerData*> peer,
Origin origin)
Origin origin,
Ui::MultiSlideTracker &mainTracker,
rpl::variable<bool> &dividerOverridden)
: _controller(controller)
, _parent(parent)
, _peer(peer)
, _origin(origin)
, _mainTracker(mainTracker)
, _dividerOverridden(dividerOverridden)
, _wrap(_parent) {
}
DetailsFiller::DetailsFiller(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<Data::SavedSublist*> sublist)
not_null<Data::SavedSublist*> sublist,
Ui::MultiSlideTracker &mainTracker,
rpl::variable<bool> &dividerOverridden)
: _controller(controller)
, _parent(parent)
, _peer(sublist->sublistPeer())
, _sublist(sublist)
, _mainTracker(mainTracker)
, _dividerOverridden(dividerOverridden)
, _wrap(_parent) {
}
DetailsFiller::DetailsFiller(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<Data::ForumTopic*> topic)
not_null<Data::ForumTopic*> topic,
Ui::MultiSlideTracker &mainTracker,
rpl::variable<bool> &dividerOverridden)
: _controller(controller)
, _parent(parent)
, _peer(topic->peer())
, _topic(topic)
, _mainTracker(mainTracker)
, _dividerOverridden(dividerOverridden)
, _wrap(_parent) {
}
@@ -1321,7 +1353,11 @@ bool SetClickContext(
}
object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
auto result = object_ptr<Ui::VerticalLayout>(_wrap);
auto wrap = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
_wrap,
object_ptr<Ui::VerticalLayout>(_wrap));
_mainTracker.track(wrap.data());
const auto result = wrap->entity();
auto tracker = Ui::MultiSlideTracker();
// Fill context for a mention / hashtag / bot command link.
@@ -1464,7 +1500,7 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
not_null<Ui::FlatLabel*> label,
int rightSkip) {
const auto parent = label->parentWidget();
const auto container = result.data();
const auto container = result;
rpl::combine(
container->widthValue(),
label->geometryValue(),
@@ -1634,13 +1670,6 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
QString()
).text->setLinksTrusted();
}
AddMainButton(
result,
tr::lng_info_add_as_contact(),
CanAddContactValue(user),
[=] { controller->window().show(Box(EditContactBox, controller, user)); },
tracker);
} else {
const auto topicRootId = _topic ? _topic->rootId() : 0;
const auto addToLink = topicRootId
@@ -1648,7 +1677,8 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
: QString();
auto linkText = LinkValue(
_peer,
true
true,
topicRootId
) | rpl::map([=](const LinkWithUrl &link) {
const auto text = link.text;
return text.isEmpty()
@@ -1661,7 +1691,7 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
});
const auto linkLine = addInfoOneLine(
(topicRootId
? tr::lng_info_link_label(Ui::Text::WithEntities)
? tr::lng_info_link_topic_label(Ui::Text::WithEntities)
: UsernamesSubtext(_peer, tr::lng_info_link_label())),
std::move(linkText),
QString());
@@ -1674,7 +1704,7 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
linkLine.subtext->overrideLinkClickHandler(linkCallback);
linkLine.text->setContextMenuHook(lnkHook);
linkLine.subtext->setContextMenuHook(lnkHook);
{
if (!topicRootId || !_peer->username().isEmpty()) {
const auto qr = Ui::CreateChild<Ui::IconButton>(
linkLine.text->parentWidget(),
st::infoProfileLabeledButtonQr);
@@ -1712,8 +1742,10 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
addTranslateToMenu(about.text, AboutWithAdvancedValue(_peer));
}
}
wrap->toggleOn(tracker.atLeastOneShownValue());
wrap->finishAnimating();
return result;
return wrap;
}
object_ptr<Ui::RpWidget> DetailsFiller::setupPersonalChannel(
@@ -2030,7 +2062,6 @@ void DetailsFiller::setupMainApp() {
UrlClickHandler::Open(url);
return false;
});
Ui::AddSkip(_wrap);
}
void DetailsFiller::setupBotPermissions() {
@@ -2060,11 +2091,11 @@ void DetailsFiller::setupBotPermissions() {
)).send();
}, emoji->lifetime());
AddSkip(_wrap);
AddDivider(_wrap);
AddSkip(_wrap);
}
void DetailsFiller::addReportReaction(Ui::MultiSlideTracker &tracker) {
void DetailsFiller::addReportReaction(
Ui::MultiSlideTracker &tracker,
Ui::MultiSlideTracker *buttonTracker) {
v::match(_origin.data, [&](GroupReactionOrigin data) {
const auto user = _peer->asUser();
if (_peer->isSelf()) {
@@ -2081,7 +2112,7 @@ void DetailsFiller::addReportReaction(Ui::MultiSlideTracker &tracker) {
const auto ban = channel->canBanMembers()
&& (!user || !channel->mgInfo->admins.contains(user->id))
&& (!user || channel->mgInfo->creator != user);
addReportReaction(data, ban, tracker);
addReportReaction(data, ban, tracker, buttonTracker);
}
}
}, [](const auto &) {});
@@ -2090,7 +2121,8 @@ void DetailsFiller::addReportReaction(Ui::MultiSlideTracker &tracker) {
void DetailsFiller::addReportReaction(
GroupReactionOrigin data,
bool ban,
Ui::MultiSlideTracker &tracker) {
Ui::MultiSlideTracker &tracker,
Ui::MultiSlideTracker *buttonTracker) {
const auto peer = _peer;
if (!peer) {
return;
@@ -2098,6 +2130,11 @@ void DetailsFiller::addReportReaction(
const auto controller = _controller->parentController();
const auto forceHidden = std::make_shared<rpl::variable<bool>>(false);
const auto user = peer->asUser();
const auto wrap = _wrap->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
_wrap.data(),
object_ptr<Ui::VerticalLayout>(_wrap.data())));
Ui::AddSkip(wrap->entity());
auto shown = user
? rpl::combine(
Info::Profile::IsContactValue(user),
@@ -2108,6 +2145,12 @@ void DetailsFiller::addReportReaction(
const auto sent = [=] {
*forceHidden = true;
};
wrap->toggleOn(rpl::duplicate(shown));
rpl::duplicate(shown) | rpl::start_with_next([=](bool shown) {
if (shown) {
_dividerOverridden.force_assign(false);
}
}, wrap->lifetime());
AddMainButton(
_wrap,
(ban
@@ -2117,38 +2160,96 @@ void DetailsFiller::addReportReaction(
[=] { controller->show(
Box(ReportReactionBox, controller, peer, data, ban, sent)); },
tracker,
buttonTracker,
st::infoMainButtonAttention);
}
void DetailsFiller::addViewChannelButton(
Ui::MultiSlideTracker &tracker,
not_null<ChannelData*> channel,
Ui::MultiSlideTracker *buttonTracker) {
using namespace rpl::mappers;
const auto wrap = _wrap->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
_wrap.data(),
object_ptr<Ui::VerticalLayout>(_wrap.data())));
Ui::AddSkip(wrap->entity());
const auto window = _controller->parentController();
auto activePeerValue = window->activeChatValue(
) | rpl::map([](Dialogs::Key key) {
return key.peer();
});
auto viewChannelVisible = rpl::combine(
_controller->wrapValue(),
std::move(activePeerValue),
(_1 != Wrap::Side) || (_2 != channel));
auto viewChannel = [=] {
window->showPeerHistory(
channel,
Window::SectionShow::Way::Forward);
};
wrap->toggleOn(rpl::duplicate(viewChannelVisible));
AddMainButton(
wrap->entity(),
tr::lng_profile_view_channel(),
std::move(viewChannelVisible),
std::move(viewChannel),
tracker,
buttonTracker);
}
object_ptr<Ui::RpWidget> DetailsFiller::fill() {
Expects(!_topic || !_topic->creating());
if (!_topic) {
} else {
add(object_ptr<Ui::BoxContentDivider>(_wrap));
}
if (const auto user = _sublist ? nullptr : _peer->asUser()) {
add(setupPersonalChannel(user));
}
add(CreateSkipWidget(_wrap));
add(CreateSlideSkipWidget(_wrap))->toggleOn(
_mainTracker.atLeastOneShownValue());
add(setupInfo());
auto lastButtonTracker = Ui::MultiSlideTracker();
if (const auto user = _peer->asUser()) {
{
const auto wrap = _wrap->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
_wrap.data(),
object_ptr<Ui::VerticalLayout>(_wrap.data())));
Ui::AddSkip(wrap->entity());
AddMainButton(
wrap->entity(),
tr::lng_info_add_as_contact(),
CanAddContactValue(user),
[=, controller = _controller->parentController()] {
controller->uiShow()->show(
Box(EditContactBox, controller, user));
},
_mainTracker,
&lastButtonTracker);
wrap->toggleOn(CanAddContactValue(user));
}
if (const auto info = user->botInfo.get()) {
if (info->hasMainApp) {
_dividerOverridden.force_assign(true);
setupMainApp();
}
if (info->canManageEmojiStatus) {
_dividerOverridden.force_assign(false);
setupBotPermissions();
}
}
if (!user->isSelf() && !_sublist) {
auto topSkip = _wrap->add(CreateSlideSkipWidget(_wrap));
Ui::MultiSlideTracker tracker;
addReportReaction(tracker);
topSkip->toggleOn(std::move(tracker).atLeastOneShownValue());
addReportReaction(_mainTracker, &lastButtonTracker);
}
} else if (const auto channel = _peer->asChannel()) {
if (!channel->isMegagroup()) {
_dividerOverridden.force_assign(false);
addViewChannelButton(_mainTracker, channel, &lastButtonTracker);
}
}
add(CreateSkipWidget(_wrap));
add(CreateSlideSkipWidget(_wrap))->toggleOn(
lastButtonTracker.atLeastOneShownValue());
return std::move(_wrap);
}
@@ -2602,24 +2703,46 @@ object_ptr<Ui::RpWidget> SetupDetails(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<PeerData*> peer,
Origin origin) {
DetailsFiller filler(controller, parent, peer, origin);
Origin origin,
Ui::MultiSlideTracker &mainTracker,
rpl::variable<bool> &dividerOverridden) {
DetailsFiller filler(
controller,
parent,
peer,
origin,
mainTracker,
dividerOverridden);
return filler.fill();
}
object_ptr<Ui::RpWidget> SetupDetails(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<Data::SavedSublist*> sublist) {
DetailsFiller filler(controller, parent, sublist);
not_null<Data::SavedSublist*> sublist,
Ui::MultiSlideTracker &mainTracker,
rpl::variable<bool> &dividerOverridden) {
DetailsFiller filler(
controller,
parent,
sublist,
mainTracker,
dividerOverridden);
return filler.fill();
}
object_ptr<Ui::RpWidget> SetupDetails(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<Data::ForumTopic*> topic) {
DetailsFiller filler(controller, parent, topic);
not_null<Data::ForumTopic*> topic,
Ui::MultiSlideTracker &mainTracker,
rpl::variable<bool> &dividerOverridden) {
DetailsFiller filler(
controller,
parent,
topic,
mainTracker,
dividerOverridden);
return filler.fill();
}
@@ -2840,20 +2963,6 @@ object_ptr<Ui::RpWidget> SetupChannelMembersAndManage(
st::infoChannelAdminsIconPosition);
}
if (EditPeerInfoBox::Available(channel)) {
const auto sessionController = controller->parentController();
const auto button = AddActionButton(
result->entity(),
tr::lng_profile_manage(),
rpl::single(true),
[=] { sessionController->showEditPeerBox(channel); },
nullptr);
object_ptr<FloatingIcon>(
button,
st::menuIconManage,
st::infoChannelAdminsIconPosition);
}
result->setDuration(st::infoSlideDuration)->toggleOn(
rpl::combine(
std::move(membersShown),
@@ -2898,13 +3007,34 @@ void AddDetails(
not_null<PeerData*> peer,
Data::ForumTopic *topic,
Data::SavedSublist *sublist,
Origin origin) {
Origin origin,
Ui::MultiSlideTracker &mainTracker,
rpl::variable<bool> &dividerOverridden) {
if (topic) {
container->add(SetupDetails(controller, container, topic));
container->add(
SetupDetails(
controller,
container,
topic,
mainTracker,
dividerOverridden));
} else if (sublist) {
container->add(SetupDetails(controller, container, sublist));
container->add(
SetupDetails(
controller,
container,
sublist,
mainTracker,
dividerOverridden));
} else {
container->add(SetupDetails(controller, container, peer, origin));
container->add(
SetupDetails(
controller,
container,
peer,
origin,
mainTracker,
dividerOverridden));
}
}

View File

@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Ui {
class RpWidget;
class VerticalLayout;
class MultiSlideTracker;
} // namespace Ui
namespace Data {
@@ -35,12 +36,20 @@ object_ptr<Ui::RpWidget> SetupDetails(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<PeerData*> peer,
Origin origin);
Origin origin,
Ui::MultiSlideTracker &mainTracker);
object_ptr<Ui::RpWidget> SetupDetails(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<Data::ForumTopic*> topic);
not_null<Data::ForumTopic*> topic,
Ui::MultiSlideTracker &mainTracker);
object_ptr<Ui::RpWidget> SetupDetails(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<Data::SavedSublist*> sublist,
Ui::MultiSlideTracker &mainTracker);
object_ptr<Ui::RpWidget> SetupActions(
not_null<Controller*> controller,
@@ -64,7 +73,9 @@ void AddDetails(
not_null<PeerData*> peer,
Data::ForumTopic *topic,
Data::SavedSublist *sublist,
Origin origin);
Origin origin,
Ui::MultiSlideTracker &mainTracker,
rpl::variable<bool> &dividerOverridden);
} // namespace Info::Profile

View File

@@ -105,7 +105,10 @@ void Badge::setContent(Content content) {
Data::EmojiStatusCustomId(id),
[raw = _view.data()] { raw->update(); },
sizeTag());
if (_customStatusLoopsLimit > 0) {
if (_content.badge == BadgeType::BotVerified) {
_emojiStatus = std::make_unique<Ui::Text::FirstFrameEmoji>(
std::move(_emojiStatus));
} else if (_customStatusLoopsLimit > 0) {
_emojiStatus = std::make_unique<Ui::Text::LimitedLoopsEmoji>(
std::move(_emojiStatus),
_customStatusLoopsLimit);
@@ -131,7 +134,7 @@ void Badge::setContent(Content content) {
}
if (icon) {
auto p = Painter(check);
if (_overrideSt) {
if (_overrideSt && !iconForeground) {
icon->paint(
p,
emoji,
@@ -142,7 +145,16 @@ void Badge::setContent(Content content) {
icon->paint(p, emoji, 0, check->width());
}
if (iconForeground) {
iconForeground->paint(p, emoji, 0, check->width());
if (_overrideSt) {
iconForeground->paint(
p,
emoji,
0,
check->width(),
_overrideSt->premiumFg->c);
} else {
iconForeground->paint(p, emoji, 0, check->width());
}
}
}
}, _view->lifetime());

View File

@@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h"
#include "data/data_saved_music.h"
#include "data/data_saved_sublist.h"
#include "info/saved/info_saved_music_common.h"
#include "info_profile_actions.h"
#include "main/main_session.h"
#include "apiwrap.h"
@@ -49,11 +50,6 @@ namespace Profile {
namespace {
[[nodiscard]] MusicButtonData DocumentMusicButtonData(
not_null<DocumentData*> document) {
return { Ui::Text::FormatSongNameFor(document) };
}
void AddAboutVerification(
not_null<Ui::VerticalLayout*> layout,
not_null<PeerData*> peer) {
@@ -97,6 +93,10 @@ InnerWidget::InnerWidget(
}, lifetime());
}
rpl::producer<> InnerWidget::backRequest() const {
return _backClicks.events();
}
object_ptr<Ui::RpWidget> InnerWidget::setupContent(
not_null<RpWidget*> parent,
Origin origin) {
@@ -121,8 +121,29 @@ object_ptr<Ui::RpWidget> InnerWidget::setupContent(
return result;
}
AddDetails(result, _controller, _peer, _topic, _sublist, origin);
result->add(setupSharedMedia(result.data()));
auto mainTracker = Ui::MultiSlideTracker();
auto sharedTracker = Ui::MultiSlideTracker();
auto dividerOverridden = rpl::variable<bool>(false);
AddDetails(
result,
_controller,
_peer,
_topic,
_sublist,
origin,
mainTracker,
dividerOverridden);
auto showDivider = rpl::combine(
mainTracker.atLeastOneShownValue(),
dividerOverridden.value()
) | rpl::map([](bool main, bool dividerOverridden) {
return dividerOverridden ? false : main;
}) | rpl::distinct_until_changed();
result->add(
setupSharedMedia(
result.data(),
rpl::duplicate(showDivider),
sharedTracker));
if (_topic || _sublist) {
return result;
}
@@ -135,24 +156,32 @@ object_ptr<Ui::RpWidget> InnerWidget::setupContent(
result->add(std::move(buttons));
}
}
auto showNext = rpl::combine(
std::move(showDivider),
sharedTracker.atLeastOneShownValue()
) | rpl::map([](bool show, bool shared) {
return show || shared;
}) | rpl::distinct_until_changed();
if (auto actions = SetupActions(_controller, result.data(), _peer)) {
addAboutVerificationOrDivider(result);
addAboutVerificationOrDivider(result, rpl::duplicate(showNext));
result->add(std::move(actions));
}
if (_peer->isChat() || _peer->isMegagroup()) {
if (!_peer->isMonoforum()) {
setupMembers(result.data());
setupMembers(result.data(), rpl::duplicate(showNext));
}
}
return result;
}
void InnerWidget::setupMembers(not_null<Ui::VerticalLayout*> container) {
void InnerWidget::setupMembers(
not_null<Ui::VerticalLayout*> container,
rpl::producer<bool> showDivider) {
auto wrap = container->add(object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
container,
object_ptr<Ui::VerticalLayout>(container)));
const auto inner = wrap->entity();
addAboutVerificationOrDivider(inner);
addAboutVerificationOrDivider(inner, std::move(showDivider));
_members = inner->add(object_ptr<Members>(inner, _controller));
_members->scrollToRequests(
) | rpl::start_with_next([this](Ui::ScrollToRequest request) {
@@ -178,66 +207,44 @@ void InnerWidget::setupMembers(not_null<Ui::VerticalLayout*> container) {
}
void InnerWidget::setupSavedMusic(not_null<Ui::VerticalLayout*> container) {
auto musicValue = Data::SavedMusic::Supported(_peer->id)
? Data::SavedMusicList(
_peer,
nullptr,
1
) | rpl::map([=](const Data::SavedMusicSlice &data) {
return data.size() ? data[0].get() : nullptr;
}) | rpl::type_erased()
: rpl::single<HistoryItem*>((HistoryItem*)(nullptr));
const auto divider = container->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
container,
object_ptr<Ui::VerticalLayout>(container)));
rpl::combine(
std::move(musicValue),
_topBarColor.value()
) | rpl::start_with_next([=](
HistoryItem *item,
std::optional<QColor> color) {
while (divider->entity()->count()) {
delete divider->entity()->widgetAt(0);
}
if (item) {
if (const auto document = item->media()
? item->media()->document()
: nullptr) {
const auto music = divider->entity()->add(
object_ptr<MusicButton>(
divider->entity(),
DocumentMusicButtonData(document),
[window = _controller, peer = _peer] {
window->showSection(Info::Saved::MakeMusic(peer));
}));
music->setOverrideBg(color);
}
divider->toggle(true, anim::type::normal);
}
}, lifetime());
divider->finishAnimating();
Info::Saved::SetupSavedMusic(
container,
_controller,
_peer,
_topBarColor.value());
}
void InnerWidget::addAboutVerificationOrDivider(
not_null<Ui::VerticalLayout*> content) {
if (_aboutVerificationAdded) {
Ui::AddDivider(content);
not_null<Ui::VerticalLayout*> content,
rpl::producer<bool> showDivider) {
if (rpl::variable<bool>(rpl::duplicate(showDivider)).current()) {
if (_aboutVerificationAdded) {
Ui::AddDivider(content);
} else {
AddAboutVerification(content, _peer);
_aboutVerificationAdded = true;
}
} else {
AddAboutVerification(content, _peer);
_aboutVerificationAdded = true;
const auto wrap = content->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
content,
object_ptr<Ui::VerticalLayout>(content)));
Ui::AddDivider(wrap->entity());
wrap->setDuration(
st::infoSlideDuration
)->toggleOn(rpl::duplicate(showDivider));
}
}
object_ptr<Ui::RpWidget> InnerWidget::setupSharedMedia(
not_null<RpWidget*> parent) {
not_null<RpWidget*> parent,
rpl::producer<bool> showDivider,
Ui::MultiSlideTracker &sharedTracker) {
using namespace rpl::mappers;
using MediaType = Media::Type;
auto content = object_ptr<Ui::VerticalLayout>(parent);
auto tracker = Ui::MultiSlideTracker();
auto &tracker = sharedTracker;
auto addMediaButton = [&](
MediaType type,
const style::icon &icon) {
@@ -358,7 +365,7 @@ object_ptr<Ui::RpWidget> InnerWidget::setupSharedMedia(
auto layout = result->entity();
addAboutVerificationOrDivider(layout);
addAboutVerificationOrDivider(layout, std::move(showDivider));
Ui::AddSkip(layout, st::infoSharedMediaBottomSkip);
layout->add(std::move(content));
Ui::AddSkip(layout, st::infoSharedMediaBottomSkip);
@@ -435,6 +442,8 @@ base::weak_qptr<Ui::RpWidget> InnerWidget::createPinnedToTop(
.backToggles = _backToggles.value(),
.showFinished = _showFinished.events(),
});
content->backRequest(
) | rpl::start_to_stream(_backClicks, content->lifetime());
content->setOnlineCount(_onlineCount.events());
_topBarColor = content->edgeColor();
return base::make_weak(not_null<Ui::RpWidget*>{ content });

View File

@@ -46,6 +46,8 @@ public:
not_null<Controller*> controller,
Origin origin);
[[nodiscard]] rpl::producer<> backRequest() const;
void saveState(not_null<Memento*> memento);
void restoreState(not_null<Memento*> memento);
@@ -71,8 +73,13 @@ private:
object_ptr<RpWidget> setupContent(
not_null<RpWidget*> parent,
Origin origin);
object_ptr<RpWidget> setupSharedMedia(not_null<RpWidget*> parent);
void setupMembers(not_null<Ui::VerticalLayout*> container);
object_ptr<RpWidget> setupSharedMedia(
not_null<RpWidget*> parent,
rpl::producer<bool> showDivider,
Ui::MultiSlideTracker &sharedTracker);
void setupMembers(
not_null<Ui::VerticalLayout*> container,
rpl::producer<bool> showDivider);
void setupSavedMusic(not_null<Ui::VerticalLayout*> container);
int countDesiredHeight() const;
@@ -80,7 +87,9 @@ private:
_desiredHeight.fire(countDesiredHeight());
}
void addAboutVerificationOrDivider(not_null<Ui::VerticalLayout*> content);
void addAboutVerificationOrDivider(
not_null<Ui::VerticalLayout*> content,
rpl::producer<bool> showDivider);
const not_null<Controller*> _controller;
const not_null<PeerData*> _peer;
@@ -93,6 +102,7 @@ private:
rpl::event_stream<int> _desiredHeight;
rpl::variable<bool> _backToggles;
rpl::event_stream<> _backClicks;
rpl::event_stream<int> _onlineCount;
rpl::event_stream<> _showFinished;

View File

@@ -8,11 +8,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/profile/info_profile_top_bar.h"
#include "api/api_peer_colors.h"
#include "api/api_peer_photo.h"
#include "api/api_user_privacy.h"
#include "apiwrap.h"
#include "base/call_delayed.h"
#include "base/timer_rpl.h"
#include "base/timer.h"
#include "base/unixtime.h"
#include "boxes/peers/edit_peer_info_box.h" // EditPeerInfoBox::Available.
#include "boxes/moderate_messages_box.h"
#include "boxes/report_messages_box.h"
#include "boxes/star_gift_box.h"
@@ -28,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document.h"
#include "data/data_emoji_statuses.h"
#include "data/data_forum_topic.h"
#include "data/data_forum.h"
#include "data/data_peer_values.h"
#include "data/data_peer.h"
#include "data/data_photo.h"
@@ -39,6 +43,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/notify/data_notify_settings.h"
#include "data/notify/data_peer_notify_settings.h"
#include "data/stickers/data_custom_emoji.h"
#include "editor/photo_editor_common.h"
#include "editor/photo_editor_layer_widget.h"
#include "history/history.h"
#include "info/info_memento.h"
#include "info/profile/info_profile_badge_tooltip.h"
@@ -47,6 +53,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/profile/info_profile_status_label.h"
#include "info/profile/info_profile_top_bar_action_button.h"
#include "info/profile/info_profile_values.h"
#include "info/userpic/info_userpic_emoji_builder_common.h"
#include "info/userpic/info_userpic_emoji_builder_common.h"
#include "info/userpic/info_userpic_emoji_builder_menu_item.h"
#include "lang/lang_keys.h"
#include "lottie/lottie_animation.h"
#include "lottie/lottie_multi_player.h"
@@ -58,15 +67,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/boxes/show_or_premium_box.h"
#include "ui/color_contrast.h"
#include "ui/controls/stars_rating.h"
#include "ui/controls/userpic_button.h"
#include "ui/effects/animations.h"
#include "ui/effects/outline_segments.h"
#include "ui/empty_userpic.h"
#include "ui/layers/generic_box.h"
#include "ui/peer/video_userpic_player.h"
#include "ui/painter.h"
#include "ui/peer/video_userpic_player.h"
#include "ui/rect.h"
#include "ui/text/text_utilities.h"
#include "ui/top_background_gradient.h"
#include "ui/ui_utility.h"
#include "ui/effects/outline_segments.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/horizontal_fit_container.h"
#include "ui/widgets/labels.h"
@@ -77,14 +88,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_peer_menu.h"
#include "window/window_session_controller.h"
#include "styles/style_boxes.h"
#include "styles/style_chat.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_chat.h"
#include "styles/style_info.h"
#include "styles/style_layers.h"
#include "styles/style_menu_icons.h"
#include "styles/style_settings.h"
#include <QGraphicsOpacityEffect>
#include <QtGui/QClipboard>
#include <QtGui/QGuiApplication>
namespace Info::Profile {
namespace {
@@ -109,11 +122,18 @@ struct PatternColors {
const std::optional<QColor> &edgeColor,
bool isDark) {
if (collectible && collectible->patternColor.isValid()) {
auto blended = Ui::BlendColors(
collectible->patternColor,
Qt::black,
isDark ? (140. / 255) : (160. / 255));
auto result = !edgeColor
? std::move(blended)
: (Ui::CountContrast(blended, *edgeColor)
> Ui::CountContrast(collectible->patternColor, *edgeColor))
? std::move(blended)
: collectible->patternColor;
return {
.patternColor = Ui::BlendColors(
collectible->patternColor,
Qt::black,
isDark ? (140. / 255) : (160. / 255)),
.patternColor = std::move(result),
// .patternColor = collectible->patternColor.lighter(isDark
// ? 140
// : 160),
@@ -148,12 +168,14 @@ struct PatternColors {
const auto acx = ax + aw / 2.;
const auto acy = ay + ah / 2.;
const auto padding24 = 24.;
const auto padding16 = 16.;
const auto padding8 = 8.;
const auto padding12 = 12.;
const auto padding48 = 48.;
const auto padding96 = 96.;
constexpr auto kPaddingScale = 0.8;
const auto padding24 = style::ConvertFloatScale(24. * kPaddingScale);
const auto padding16 = style::ConvertFloatScale(16. * kPaddingScale);
const auto padding8 = style::ConvertFloatScale(8. * kPaddingScale);
const auto padding12 = style::ConvertFloatScale(12. * kPaddingScale);
const auto padding48 = style::ConvertFloatScale(48. * kPaddingScale);
const auto padding96 = style::ConvertFloatScale(96. * kPaddingScale);
static const auto kCos120 = std::cos(M_PI * 120. / 180.);
static const auto kCos160 = std::cos(M_PI * 160. / 180.);
const auto r48Cos120 = (padding48 + aw / 2.) * kCos120;
@@ -245,7 +267,7 @@ TopBar::TopBar(
? 0
: st::infoProfileTopBarActionButtonsHeight);
}())
, _title(this, Info::Profile::NameValue(_peer), _st.title)
, _title(this, nameValue(), _st.title)
, _starsRating(_peer->isUser()
? std::make_unique<Ui::StarsRating>(
this,
@@ -259,9 +281,41 @@ TopBar::TopBar(
, _status(this, QString(), statusStyle())
, _statusLabel(std::make_unique<StatusLabel>(_status.data(), _peer))
, _showLastSeen(
this,
tr::lng_status_lastseen_when(),
st::infoProfileTopBarShowLastSeen)
, _forumButton([&, controller = descriptor.controller] {
const auto topic = _key.topic();
if (!topic) {
return object_ptr<Ui::RoundButton>{ nullptr };
}
auto owned = object_ptr<Ui::RoundButton>(
this,
tr::lng_status_lastseen_when(),
st::infoProfileCover.showLastSeen) {
rpl::single(QString()),
st::infoProfileTopBarTopicStatusButton);
owned->setText(Info::Profile::NameValue(
_peer
) | rpl::map([=](const QString &name) {
return TextWithEntities(name)
.append(' ')
.append(Ui::Text::IconEmoji(&st::textMoreIconEmoji, QString()));
}));
owned->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
owned->setClickedCallback([=, peer = _peer] {
if (const auto forum = peer->forum()) {
if (peer->useSubsectionTabs()) {
controller->searchInChat(forum->history());
} else if (controller->adaptive().isOneColumn()) {
controller->showForum(forum);
} else {
controller->showPeerHistory(peer->id);
}
} else {
controller->showPeerHistory(peer->id);
}
});
return owned;
}()) {
const auto controller = descriptor.controller;
if (_peer->isMegagroup() || _peer->isChat()) {
@@ -277,11 +331,13 @@ TopBar::TopBar(
: std::make_shared<Info::Memento>(shown, section));
});
}
if (!_peer->isMegagroup()) {
if (!_peer->isMegagroup() && !_topic) {
setupStatusWithRating();
}
setupShowLastSeen(controller);
if (!_topic) {
setupShowLastSeen(controller);
}
_peer->session().changes().peerFlagsValue(
_peer,
@@ -290,11 +346,18 @@ TopBar::TopBar(
_statusLabel->refresh();
}, lifetime());
_title->setSelectable(true);
_title->setContextCopyText(tr::lng_profile_copy_fullname(tr::now));
auto badgeUpdates = rpl::producer<rpl::empty_value>();
if (_badge) {
badgeUpdates = rpl::merge(
std::move(badgeUpdates),
_badge->updated());
_badge->setPremiumClickCallback([controller, peer = _peer] {
::Settings::ShowEmojiStatusPremium(controller, peer);
});
}
if (_verified) {
badgeUpdates = rpl::merge(
@@ -306,6 +369,17 @@ TopBar::TopBar(
std::move(badgeUpdates),
_botVerify->updated());
}
badgeUpdates = rpl::merge(
std::move(badgeUpdates),
nameValue() | rpl::map([=](const QString &name) {
const auto emojiCount = ranges::count(name, true, [](QChar ch) {
return ch.isHighSurrogate();
});
_title->resizeToWidth(_title->st().style.font->width(name)
+ emojiCount);
return rpl::empty_value();
}),
rpl::duplicate(descriptor.backToggles) | rpl::to_empty);
std::move(badgeUpdates) | rpl::start_with_next([=] {
updateLabelsPosition();
}, _title->lifetime());
@@ -313,13 +387,14 @@ TopBar::TopBar(
setupUniqueBadgeTooltip();
setupButtons(
controller,
descriptor.backToggles.value(),
rpl::duplicate(descriptor.backToggles),
descriptor.source);
setupUserpicButton(controller);
if (_hasActions) {
_peer->session().changes().peerFlagsValue(
_peer,
Data::PeerUpdate::Flag::FullInfo
| Data::PeerUpdate::Flag::ChannelAmIn
) | rpl::start_with_next([=] {
setupActions(controller);
}, lifetime());
@@ -373,6 +448,10 @@ TopBar::TopBar(
) | rpl::take(1) | rpl::start_with_next([=] {
setupPinnedToTopGifts(controller);
}, lifetime());
if (_forumButton) {
_forumButton->show();
}
}
void TopBar::adjustColors(const std::optional<QColor> &edgeColor) {
@@ -386,6 +465,16 @@ void TopBar::adjustColors(const std::optional<QColor> &edgeColor) {
_title->setTextColorOverride(shouldOverrideTitle
? std::optional<QColor>(st::groupCallMembersFg->c)
: std::nullopt);
if (!_showLastSeen->isHidden()) {
if (shouldOverrideTitle) {
const auto st = mapActionStyle(edgeColor);
_showLastSeen->setBrushOverride(st.bgColor);
_showLastSeen->setTextFgOverride(st.fgColor);
} else {
_showLastSeen->setBrushOverride(std::nullopt);
_showLastSeen->setTextFgOverride(std::nullopt);
}
}
{
const auto membersLinkCallback = _statusLabel->membersLinkCallback();
{
@@ -405,7 +494,7 @@ void TopBar::adjustColors(const std::optional<QColor> &edgeColor) {
_status.create(this, QString(), statusStyle());
}
_status->show();
if (!_peer->isMegagroup()) {
if (!_peer->isMegagroup() && !_topic) {
setupStatusWithRating();
}
_status->widthValue() | rpl::start_with_next([=] {
@@ -473,7 +562,7 @@ void TopBar::updateCollectibleStatus() {
_patternEmoji = document->owner().customEmojiManager().create(
document,
[=] { update(); },
Data::CustomEmojiSizeTag::Large);
Data::CustomEmojiSizeTag::Normal);
} else {
_patternEmoji = nullptr;
}
@@ -484,7 +573,7 @@ void TopBar::updateCollectibleStatus() {
_pinnedToTopGifts.clear();
}
if (colorProfile && !colorProfile->palette.empty()) {
const auto copySt = [&](const style::InfoPeerBadge &st) {
const auto copyStVerified = [&](const style::InfoPeerBadge &st) {
auto result = std::make_unique<style::InfoPeerBadge>(
base::duplicate(st));
auto fg = std::make_shared<style::owned_color>(
@@ -496,10 +585,17 @@ void TopBar::updateCollectibleStatus() {
return std::shared_ptr<style::InfoPeerBadge>(
result.release(),
[fg](style::InfoPeerBadge *ptr) { delete ptr; });
return std::shared_ptr<style::InfoPeerBadge>(result.release());
};
const auto copySt = [&](const style::InfoPeerBadge &st) {
auto result = std::make_unique<style::InfoPeerBadge>(
base::duplicate(st));
result->premiumFg = st::groupCallVideoSubTextFg;
return std::shared_ptr<style::InfoPeerBadge>(result.release());
};
_botVerifySt = copySt(st::infoColoredBotVerifyBadge);
_badgeSt = copySt(st::infoColoredPeerBadge);
_verifiedSt = copySt(st::infoColoredPeerBadge);
_verifiedSt = copyStVerified(st::infoColoredPeerBadge);
} else {
_botVerifySt = nullptr;
_badgeSt = nullptr;
@@ -521,27 +617,7 @@ void TopBar::setupActions(not_null<Window::SessionController*> controller) {
const auto topic = _key.topic();
const auto sublist = _key.sublist();
const auto isSide = (_wrap.current() == Wrap::Side);
const auto mapped = [=](std::optional<QColor> c) {
if (c) {
return TopBarActionButtonStyle{
.bgColor = Ui::BlendColors(
*c,
Qt::black,
st::infoProfileTopBarActionButtonBgOpacity),
.fgColor = std::make_optional(st::premiumButtonFg->c),
.shadowColor = std::nullopt,
};
} else {
return TopBarActionButtonStyle{
.bgColor = anim::with_alpha(
st::boxBg->c,
1. - st::infoProfileTopBarActionButtonBgOpacity),
.fgColor = std::nullopt,
.shadowColor = std::make_optional(
st::windowShadowFgFallback->c),
};
}
};
auto buttons = std::vector<not_null<TopBarActionButton*>>();
_actions = base::make_unique_q<Ui::HorizontalFitContainer>(
this,
@@ -572,7 +648,14 @@ void TopBar::setupActions(not_null<Window::SessionController*> controller) {
};
const auto guard = gsl::finally([&] {
addMore();
_edgeColor.value() | rpl::map(mapped) | rpl::start_with_next([=](
style::PaletteChanged(
) | rpl::start_with_next([=] {
const auto current = _edgeColor.current();
_edgeColor.force_assign(current);
}, _actions->lifetime());
_edgeColor.value() | rpl::map([=](std::optional<QColor> c) {
return mapActionStyle(c);
}) | rpl::start_with_next([=](
TopBarActionButtonStyle st) {
for (const auto &button : buttons) {
button->setStyle(st);
@@ -734,6 +817,20 @@ void TopBar::setupActions(not_null<Window::SessionController*> controller) {
if (chechMax()) {
return;
}
if (EditPeerInfoBox::Available(peer)) {
const auto manage = Ui::CreateChild<TopBarActionButton>(
this,
tr::lng_profile_action_short_manage(tr::now),
st::infoProfileTopBarActionManage);
manage->setClickedCallback([=, window = controller] {
window->showEditPeerBox(peer);
});
buttons.push_back(manage);
_actions->add(manage);
}
if (chechMax()) {
return;
}
{
const auto channel = peer->asBroadcast();
if (!user && !channel) {
@@ -824,19 +921,198 @@ void TopBar::setupUserpicButton(
const auto menu = _userpicButton->lifetime().make_state<
base::unique_qptr<Ui::PopupMenu>
>();
const auto canReport = [=, peer = _peer] {
if (!peer->hasUserpic()) {
return false;
}
const auto user = peer->asUser();
if (!user) {
return false;
} else if (user->hasPersonalPhoto()
|| user->isSelf()
|| user->isInaccessible()
|| user->isRepliesChat()
|| user->isVerifyCodes()
|| (user->botInfo && user->botInfo->canEditInformation)
|| user->isServiceUser()) {
return false;
}
return true;
};
const auto isContact = [=, peer = _peer] {
if (const auto user = peer->asUser()) {
return user->isContact()
&& !user->isSelf()
&& !user->isInaccessible()
&& !user->isServiceUser();
}
return false;
};
const auto canSuggestPhoto = [=, peer = _peer] {
if (const auto user = peer->asUser()) {
return !user->isSelf()
&& !user->isBot()
&& !user->starsPerMessageChecked()
&& user->owner().history(user)->lastServerMessage();
}
return false;
};
const auto choosePhotoCallback = [=](Ui::UserpicButton::ChosenType type) {
return [=](QImage &&image) {
using ChosenType = Ui::UserpicButton::ChosenType;
auto result = Api::PeerPhoto::UserPhoto{
std::move(image),
0,
std::vector<QColor>(),
};
switch (type) {
case ChosenType::Set:
_peer->session().api().peerPhoto().upload(
_peer,
std::move(result));
break;
case ChosenType::Suggest:
_peer->session().api().peerPhoto().suggest(
_peer,
std::move(result));
break;
}
};
};
const auto editorData = [=](Ui::UserpicButton::ChosenType type) {
const auto user = _peer->asUser();
const auto name = (user && !user->firstName.isEmpty())
? user->firstName
: _peer->name();
const auto phrase = (type == Ui::UserpicButton::ChosenType::Suggest)
? &tr::lng_profile_suggest_sure
: &tr::lng_profile_set_personal_sure;
return Editor::EditorData{
.about = (*phrase)(
tr::now,
lt_user,
Ui::Text::Bold(name),
Ui::Text::WithEntities),
.confirm = ((type == Ui::UserpicButton::ChosenType::Suggest)
? tr::lng_profile_suggest_button(tr::now)
: tr::lng_profile_set_photo_button(tr::now)),
.cropType = Editor::EditorData::CropType::Ellipse,
.keepAspectRatio = true,
};
};
const auto chooseFile = [=](Ui::UserpicButton::ChosenType type) {
base::call_delayed(
st::defaultRippleAnimation.hideDuration,
crl::guard(this, [=] {
Editor::PrepareProfilePhotoFromFile(
this,
&controller->window(),
editorData(type),
choosePhotoCallback(type));
}));
};
const auto addFromClipboard = [=](
Ui::PopupMenu *menu,
Ui::UserpicButton::ChosenType type,
tr::phrase<> text) {
if (const auto data = QGuiApplication::clipboard()->mimeData()) {
if (data->hasImage()) {
auto openEditor = crl::guard(this, [=] {
Editor::PrepareProfilePhoto(
this,
&controller->window(),
editorData(type),
choosePhotoCallback(type),
qvariant_cast<QImage>(data->imageData()));
});
menu->addAction(
std::move(text)(tr::now),
std::move(openEditor),
&st::menuIconPhoto);
}
}
};
_userpicButton->clicks() | rpl::start_with_next([=](
Qt::MouseButton button) {
if (button == Qt::RightButton
&& _hasStories
&& (_hasStories || canReport() || isContact())
&& _peer->userpicPhotoId()) {
*menu = base::make_unique_q<Ui::PopupMenu>(
this,
st::popupMenuExpandedSeparator);
st::popupMenuWithIcons);
(*menu)->addAction(
tr::lng_profile_open_photo(tr::now),
openPhoto,
&st::menuIconPhoto);
if (_hasStories) {
(*menu)->addAction(
tr::lng_profile_open_photo(tr::now),
openPhoto,
&st::menuIconPhoto);
}
if (canReport()) {
(*menu)->addAction(
tr::lng_profile_report(tr::now),
[=] {
controller->show(
ReportProfilePhotoBox(
_peer,
_peer->owner().photo(
_peer->userpicPhotoId())));
},
&st::menuIconReport);
}
if (isContact()) {
if (!(*menu)->empty()) {
(*menu)->addSeparator(&st::expandedMenuSeparator);
}
(*menu)->addAction(
tr::lng_profile_set_photo_for(tr::now),
[=] { chooseFile(Ui::UserpicButton::ChosenType::Set); },
&st::menuIconPhotoSet);
addFromClipboard(
menu->get(),
Ui::UserpicButton::ChosenType::Set,
tr::lng_profile_set_photo_for_from_clipboard);
if (canSuggestPhoto()) {
(*menu)->addAction(
tr::lng_profile_suggest_photo(tr::now),
[=] {
chooseFile(
Ui::UserpicButton::ChosenType::Suggest);
},
&st::menuIconPhotoSuggest);
addFromClipboard(
menu->get(),
Ui::UserpicButton::ChosenType::Suggest,
tr::lng_profile_suggest_photo_from_clipboard);
}
if (controller) {
const auto done = [=](UserpicBuilder::Result data) {
auto result = Api::PeerPhoto::UserPhoto{
base::take(data.image),
data.id,
std::move(data.colors),
};
_peer->session().api().peerPhoto().upload(
_peer,
std::move(result));
};
UserpicBuilder::AddEmojiBuilderAction(
controller,
menu->get(),
_peer->session().api().peerPhoto().emojiListValue(
Api::PeerPhoto::EmojiListType::Profile),
done,
false);
}
}
(*menu)->popup(QCursor::pos());
} else if (button == Qt::LeftButton) {
@@ -914,6 +1190,10 @@ TopBar::~TopBar() {
base::take(_badgeOldTooltips);
}
rpl::producer<> TopBar::backRequest() const {
return _backClicks.events();
}
void TopBar::setOnlineCount(rpl::producer<int> &&count) {
std::move(count) | rpl::start_with_next([=](int v) {
if (_statusLabel) {
@@ -922,14 +1202,6 @@ void TopBar::setOnlineCount(rpl::producer<int> &&count) {
}, lifetime());
}
void TopBar::setEnableBackButtonValue(rpl::producer<bool> &&producer) {
std::move(
producer
) | rpl::start_with_next([=](bool value) {
updateLabelsPosition();
}, lifetime());
}
void TopBar::setRoundEdges(bool value) {
_roundEdges = value;
update();
@@ -1034,19 +1306,24 @@ void TopBar::updateLabelsPosition() {
0,
rightButtonsWidth,
1. - progressCurrent);
const auto titleMostLeft = TopBar::titleMostLeft();
const auto interpolatedPadding = anim::interpolate(
titleMostLeft(),
titleMostLeft,
rect::m::sum::h(st::boxRowPadding),
progressCurrent);
auto titleWidth = width() - interpolatedPadding - reservedRight;
const auto verifiedWidget = _verified ? _verified->widget() : nullptr;
const auto badgeWidget = _badge ? _badge->widget() : nullptr;
const auto botVerifyWidget = _botVerify ? _botVerify->widget() : nullptr;
if (verifiedWidget) {
titleWidth -= verifiedWidget->width();
}
if (badgeWidget) {
titleWidth -= badgeWidget->width();
}
if (botVerifyWidget) {
titleWidth -= botVerifyWidget->width();
}
if (verifiedWidget || badgeWidget) {
titleWidth -= st::infoVerifiedCheckPosition.x();
}
@@ -1065,7 +1342,6 @@ void TopBar::updateLabelsPosition() {
const auto margins = LargeCustomEmojiMargins();
auto totalElementsWidth = _title->width();
const auto botVerifyWidget = _botVerify ? _botVerify->widget() : nullptr;
const auto botVerifySkip = botVerifyWidget
? botVerifyWidget->width() + st::infoVerifiedCheckPosition.x()
: 0;
@@ -1081,7 +1357,7 @@ void TopBar::updateLabelsPosition() {
totalElementsWidth += botVerifySkip;
auto titleLeft = anim::interpolate(
titleMostLeft(),
titleMostLeft,
(width() - totalElementsWidth) / 2,
progressCurrent);
@@ -1122,6 +1398,24 @@ void TopBar::updateLabelsPosition() {
}
void TopBar::updateStatusPosition(float64 progressCurrent) {
if (_forumButton) {
const auto buttonTop = anim::interpolate(
_st.subtitlePosition.y(),
st::infoProfileTopBarStatusTop,
progressCurrent);
const auto buttonLeft = anim::interpolate(
statusMostLeft(),
(width() - _forumButton->width()) / 2,
progressCurrent);
_forumButton->moveToLeft(buttonLeft, buttonTop);
_forumButton->setVisible(true);
_status->hide();
// _starsRating->hide();
_showLastSeen->hide();
return;
}
const auto statusTop = anim::interpolate(
_st.subtitlePosition.y(),
st::infoProfileTopBarStatusTop,
@@ -1135,7 +1429,7 @@ void TopBar::updateStatusPosition(float64 progressCurrent) {
progressCurrent);
if (const auto rating = _starsRating.get()) {
rating->moveTo(statusLeft, statusTop);
rating->moveTo(statusLeft, statusTop - st::lineWidth);
rating->setOpacity(progressCurrent);
}
const auto statusShift = _statusShift.current()
@@ -1148,8 +1442,8 @@ void TopBar::updateStatusPosition(float64 progressCurrent) {
statusLeft
+ statusShift
+ _status->textMaxWidth()
+ st::infoProfileTopBarLastSeenSkip,
statusTop);
+ st::infoProfileTopBarLastSeenSkip.x(),
statusTop + st::infoProfileTopBarLastSeenSkip.y());
if (_showLastSeenOpacity) {
_showLastSeenOpacity->setOpacity(progressCurrent);
}
@@ -1313,9 +1607,17 @@ void TopBar::paintEvent(QPaintEvent *e) {
if (_patternEmoji && _patternEmoji->ready()) {
paintAnimatedPattern(p, rect(), geometry);
}
paintPinnedToTopGifts(p, rect(), geometry);
paintUserpic(p, geometry);
paintStoryOutline(p, geometry);
const auto clipBounds = e->region().boundingRect();
if (clipBounds.bottom() >= geometry.top()
&& clipBounds.top() <= geometry.bottom()) {
paintPinnedToTopGifts(p, rect(), geometry);
}
if (clipBounds.intersects(geometry)) {
paintUserpic(p, geometry);
paintStoryOutline(p, geometry);
}
}
void TopBar::setupButtons(
@@ -1355,13 +1657,12 @@ void TopBar::setupButtons(
st::infoTopBarScale);
_back->QWidget::show();
_back->setDuration(0);
_back->toggleOn(isLayer
_back->toggleOn(isLayer || isSide
? rpl::duplicate(backToggles)
: rpl::single(wrap == Wrap::Narrow));
setEnableBackButtonValue(_back->toggledValue());
_back->entity()->addClickHandler([=] {
controller->showBackFromStack();
});
_back->entity()->clicks() | rpl::to_empty | rpl::start_to_stream(
_backClicks,
_back->lifetime());
if (!isLayer && !isSide) {
_close = nullptr;
@@ -1597,8 +1898,8 @@ void TopBar::paintAnimatedPattern(
_edgeColor.current(),
Window::Theme::IsNightMode());
const auto ratio = style::DevicePixelRatio();
const auto scale = 0.75;
const auto size = Ui::Emoji::GetSizeNormal() * scale;
const auto scale = 0.910;
const auto size = st::emojiSize;
_basePatternImage = QImage(
QSize(size, size) * ratio,
QImage::Format_ARGB32_Premultiplied);
@@ -1606,22 +1907,21 @@ void TopBar::paintAnimatedPattern(
_basePatternImage.fill(Qt::transparent);
auto painter = QPainter(&_basePatternImage);
auto hq = PainterHighQualityEnabler(painter);
const auto fullSize = Ui::Emoji::GetSizeNormal();
const auto offset = (fullSize - fullSize * scale) / 2;
painter.translate(offset, offset);
// const auto contentSize = size * scale;
// const auto offset = (size - contentSize) / 2.;
// painter.translate(offset, offset);
painter.scale(scale, scale);
_patternEmoji->paint(painter, { .textColor = Qt::white });
painter.resetTransform();
if (patternColors.useOverlayBlend) {
painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
painter.fillRect(
QRect(QPoint(), _basePatternImage.size() / ratio),
Rect(Size(size)),
QColor(0, 0, 0, int(0.8 * 255)));
} else {
painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
painter.fillRect(
QRect(QPoint(), _basePatternImage.size() / ratio),
patternColors.patternColor);
painter.fillRect(Rect(Size(size)), patternColors.patternColor);
}
}
@@ -1825,7 +2125,7 @@ void TopBar::setupNewGifts(
entry.animation = LottieAnimationFromDocument(
_lottiePlayer.get(),
entry.media.get(),
StickerLottieSize::StickerSet,
StickerLottieSize::PinnedProfileUniqueGiftSize,
Size(st::infoProfileTopBarGiftSize)
* style::DevicePixelRatio());
} else if (!entry.media->loaded()) {
@@ -1941,9 +2241,6 @@ void TopBar::paintPinnedToTopGifts(
? _progress.current() * _giftsAppearing->value(0.)
: _progress.current());
const auto sz = st::infoProfileTopBarGiftSize;
const auto halfSz = sz / 2.;
for (auto &gift : _pinnedToTopGifts) {
if (!gift.animation
&& (_lottieSingleLoop ? gift.lastFrame.isNull() : true)) {
@@ -1966,6 +2263,7 @@ void TopBar::paintPinnedToTopGifts(
frameToRender = gift.lastFrame;
} else if (gift.animation && gift.animation->ready()) {
frameToRender = gift.animation->frame();
frameToRender.setDevicePixelRatio(style::DevicePixelRatio());
if (_lottiePlayer) {
_lottiePlayer->markFrameShown();
}
@@ -1990,22 +2288,21 @@ void TopBar::paintPinnedToTopGifts(
}
}
if (!frameToRender.isNull()) {
const auto resultRect = QRect(
QPoint(giftPos.x() - halfSz, giftPos.y() - halfSz),
QSize(sz, sz));
const auto frameSize = frameToRender.width()
/ style::DevicePixelRatio();
const auto halfFrameSize = frameSize / 2.;
const auto resultPos = QPointF(
giftPos.x() - halfFrameSize,
giftPos.y() - halfFrameSize);
if (!gift.bg.isNull()) {
const auto bgSize = gift.bg.size()
/ gift.bg.devicePixelRatio();
const auto bgRect = QRect(
resultRect.x()
+ (resultRect.width() - bgSize.width()) / 2,
resultRect.y()
+ (resultRect.height() - bgSize.height()) / 2,
bgSize.width(),
bgSize.height());
p.drawImage(bgRect, gift.bg);
const auto bgSize = gift.bg.width()
/ style::DevicePixelRatio();
const auto bgPos = QPointF(
resultPos.x() + (frameSize - bgSize) / 2.,
resultPos.y() + (frameSize - bgSize) / 2.);
p.drawImage(bgPos, gift.bg);
}
p.drawImage(resultRect, frameToRender);
p.drawImage(resultPos, frameToRender);
}
}
p.setOpacity(1.);
@@ -2169,4 +2466,33 @@ const style::FlatLabel &TopBar::statusStyle() const {
: st::infoProfileCover.status;
}
rpl::producer<QString> TopBar::nameValue() const {
if (const auto topic = _key.topic()) {
return Info::Profile::TitleValue(topic);
}
return Info::Profile::NameValue(_peer);
}
TopBarActionButtonStyle TopBar::mapActionStyle(
std::optional<QColor> c) const {
if (c) {
return TopBarActionButtonStyle{
.bgColor = Ui::BlendColors(
*c,
Qt::black,
st::infoProfileTopBarActionButtonBgOpacity),
.fgColor = std::make_optional(st::premiumButtonFg->c),
.shadowColor = std::nullopt,
};
} else {
return TopBarActionButtonStyle{
.bgColor = anim::with_alpha(
st::boxBg->c,
1. - st::infoProfileTopBarActionButtonBgOpacity),
.fgColor = std::nullopt,
.shadowColor = std::make_optional(st::windowShadowFgFallback->c),
};
}
}
} // namespace Info::Profile

View File

@@ -82,6 +82,8 @@ namespace Info::Profile {
class Badge;
class StatusLabel;
struct TopBarActionButtonStyle;
class TopBar final : public Ui::RpWidget {
public:
enum class Source {
@@ -96,7 +98,7 @@ public:
rpl::producer<Wrap> wrap;
Source source = Source::Profile;
PeerData *peer = nullptr;
rpl::variable<bool> backToggles;
rpl::producer<bool> backToggles;
rpl::producer<> showFinished;
};
@@ -110,6 +112,8 @@ public:
TopBar(not_null<Ui::RpWidget*> parent, Descriptor descriptor);
~TopBar();
[[nodiscard]] rpl::producer<> backRequest() const;
void setOnlineCount(rpl::producer<int> &&count);
void setRoundEdges(bool value);
@@ -186,6 +190,10 @@ private:
void updateStatusPosition(float64 progressCurrent);
[[nodiscard]] const style::FlatLabel &statusStyle() const;
void setupStatusWithRating();
[[nodiscard]] TopBarActionButtonStyle mapActionStyle(
std::optional<QColor> c) const;
[[nodiscard]] rpl::producer<QString> nameValue() const;
[[nodiscard]] auto effectiveColorProfile()
const -> std::optional<Data::ColorProfileSet>;
@@ -219,6 +227,7 @@ private:
std::unique_ptr<StatusLabel> _statusLabel;
rpl::variable<int> _statusShift = 0;
object_ptr<Ui::RoundButton> _showLastSeen = { nullptr };
object_ptr<Ui::RoundButton> _forumButton = { nullptr };
QGraphicsOpacityEffect *_showLastSeenOpacity = nullptr;
std::shared_ptr<style::FlatLabel> _statusSt;
@@ -250,6 +259,8 @@ private:
base::unique_qptr<Ui::IconButton> _close;
base::unique_qptr<Ui::FadeWrap<Ui::IconButton>> _back;
rpl::event_stream<> _backClicks;
base::unique_qptr<Ui::IconButton> _topBarButton;
base::unique_qptr<Ui::PopupMenu> _peerMenu;

View File

@@ -133,19 +133,20 @@ void TopBarActionButton::paintEvent(QPaintEvent *e) {
: 0.0;
p.setOpacity(iconScale);
p.save();
const auto iconLeft = (width() - iconSize) / 2;
const auto half = iconSize / 2;
const auto iconCenter = QPoint(iconLeft + half, iconTop + half);
const auto iconLeft = (width() - iconSize) / 2.;
const auto half = iconSize / 2.;
const auto iconCenter = QPointF(iconLeft + half, iconTop + half);
p.translate(iconCenter);
p.scale(iconScale, iconScale);
p.translate(-iconCenter);
p.translate(iconLeft, iconTop);
if (_lottie) {
_lottie->paint(p, iconLeft, iconTop, _fgColor);
_lottie->paint(p, 0, 0, _fgColor);
} else if (_icon) {
if (_fgColor) {
_icon->paint(p, iconLeft, iconTop, width(), *_fgColor);
_icon->paint(p, 0, 0, width(), *_fgColor);
} else {
_icon->paint(p, iconLeft, iconTop, width());
_icon->paint(p, 0, 0, width());
}
}
p.restore();

View File

@@ -243,11 +243,42 @@ rpl::producer<TextWithEntities> AboutValue(not_null<PeerData*> peer) {
});
}
rpl::producer<LinkWithUrl> LinkValue(not_null<PeerData*> peer, bool primary) {
QString TopicLink(not_null<Data::ForumTopic*> topic, bool full) {
const auto channel = topic->channel();
const auto id = topic->rootId();
const auto base = channel->hasUsername()
? channel->username()
: "c/" + QString::number(peerToChannel(channel->id).bare);
return channel->session().createInternalLinkFull(full
? base + '/' + QString::number(id.bare)
: base);
}
rpl::producer<LinkWithUrl> LinkValue(
not_null<PeerData*> peer,
bool primary,
MsgId rootId) {
return (primary
? PlainPrimaryUsernameValue(peer)
: PlainUsernameValue(peer) | rpl::type_erased()
) | rpl::map([=](QString &&username) {
if (username.isEmpty()) {
if (const auto topic
= rootId ? peer->forumTopicFor(rootId) : nullptr) {
const auto link = TopicLink(topic, false);
return LinkWithUrl{
.text = link,
.url = link,
};
} else {
return LinkWithUrl{};
}
} else {
return LinkWithUrl{
.text = peer->session().createInternalLinkFull(username),
.url = UsernameUrl(peer, username, true),
};
}
return LinkWithUrl{
.text = (username.isEmpty()
? QString()

View File

@@ -75,9 +75,13 @@ struct LinkWithUrl {
QString text;
QString url;
};
[[nodiscard]] QString TopicLink(
not_null<Data::ForumTopic*> topic,
bool full);
[[nodiscard]] rpl::producer<LinkWithUrl> LinkValue(
not_null<PeerData*> peer,
bool primary = false);
bool primary = false,
MsgId topicRootId = 0);
[[nodiscard]] rpl::producer<const ChannelLocation*> LocationValue(
not_null<ChannelData*> channel);

View File

@@ -115,6 +115,10 @@ Widget::Widget(
}
}, lifetime());
_inner->backRequest() | rpl::start_with_next([=] {
checkBeforeClose([=] { controller->showBackFromStack(); });
}, _inner->lifetime());
if (_pinnedToTop) {
_inner->widthValue(
) | rpl::start_with_next([=](int w) {

View File

@@ -0,0 +1,83 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "info/saved/info_saved_music_common.h"
#include "data/data_peer.h"
#include "data/data_saved_music.h"
#include "data/data_saved_sublist.h"
#include "history/history_item.h"
#include "info/info_controller.h"
#include "info/info_memento.h"
#include "info/profile/info_profile_music_button.h"
#include "info/saved/info_saved_music_widget.h"
#include "ui/text/format_song_document_name.h"
#include "ui/widgets/buttons.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/vertical_list.h"
namespace Info::Saved {
namespace {
[[nodiscard]] Profile::MusicButtonData DocumentMusicButtonData(
not_null<DocumentData*> document) {
return { Ui::Text::FormatSongNameFor(document) };
}
} // namespace
void SetupSavedMusic(
not_null<Ui::VerticalLayout*> container,
not_null<Info::Controller*> controller,
not_null<PeerData*> peer,
rpl::producer<std::optional<QColor>> topBarColor) {
auto musicValue = Data::SavedMusic::Supported(peer->id)
? Data::SavedMusicList(
peer,
nullptr,
1
) | rpl::map([=](const Data::SavedMusicSlice &data) {
return data.size() ? data[0].get() : nullptr;
}) | rpl::type_erased()
: rpl::single<HistoryItem*>((HistoryItem*)(nullptr));
const auto divider = container->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
container,
object_ptr<Ui::VerticalLayout>(container)));
rpl::combine(
std::move(musicValue),
std::move(topBarColor)
) | rpl::start_with_next([=](
HistoryItem *item,
std::optional<QColor> color) {
while (divider->entity()->count()) {
delete divider->entity()->widgetAt(0);
}
if (item) {
if (const auto document = item->media()
? item->media()->document()
: nullptr) {
const auto music = divider->entity()->add(
object_ptr<Profile::MusicButton>(
divider->entity(),
DocumentMusicButtonData(document),
[window = controller, peer] {
window->showSection(Info::Saved::MakeMusic(peer));
}));
music->setOverrideBg(color);
}
divider->toggle(true, anim::type::normal);
}
}, container->lifetime());
divider->finishAnimating();
}
} // namespace Info::Saved

View File

@@ -7,6 +7,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class PeerData;
namespace Ui {
class VerticalLayout;
} // namespace Ui
namespace Info {
class Controller;
} // namespace Info
namespace Info::Saved {
struct MusicTag {
@@ -17,4 +27,10 @@ struct MusicTag {
not_null<PeerData*> peer;
};
void SetupSavedMusic(
not_null<Ui::VerticalLayout*> container,
not_null<Info::Controller*> controller,
not_null<PeerData*> peer,
rpl::producer<std::optional<QColor>> topBarColor);
} // namespace Info::Saved

View File

@@ -579,7 +579,7 @@ void FillLoading(
: u"stats"_q;
auto icon = ::Settings::CreateLottieIcon(
content,
{ .name = iconName, .sizeOverride = Size(st::changePhoneIconSize) },
{ .name = iconName, .sizeOverride = st::normalBoxLottieSize },
st::settingsBlockedListIconPadding);
(

View File

@@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/profile/info_profile_top_bar.h"
#include "info/profile/info_profile_values.h"
#include "info/profile/info_profile_widget.h"
#include "info/saved/info_saved_music_common.h"
#include "info/stories/info_stories_albums.h"
#include "info/stories/info_stories_widget.h"
#include "info/info_controller.h"
@@ -263,6 +264,10 @@ void InnerWidget::setupAlbums() {
InnerWidget::~InnerWidget() = default;
rpl::producer<> InnerWidget::backRequest() const {
return _backClicks.events();
}
void InnerWidget::setupTop() {
const auto albumId = _albumId.current();
if (_addingToAlbumId) {
@@ -293,8 +298,24 @@ void InnerWidget::startTop() {
void InnerWidget::createProfileTop() {
startTop();
Info::Saved::SetupSavedMusic(
_top,
_controller,
_peer,
_topBarColor.value());
using namespace Profile;
AddDetails(_top, _controller, _peer, nullptr, nullptr, { v::null });
auto mainTracker = Ui::MultiSlideTracker();
auto dividerOverridden = rpl::variable<bool>(false);
AddDetails(
_top,
_controller,
_peer,
nullptr,
nullptr,
{ v::null },
mainTracker,
dividerOverridden);
auto tracker = Ui::MultiSlideTracker();
const auto dividerWrap = _top->add(
@@ -504,6 +525,8 @@ base::weak_qptr<Ui::RpWidget> InnerWidget::createPinnedToTop(
.backToggles = _backToggles.value(),
.showFinished = _showFinished.events(),
});
content->backRequest(
) | rpl::start_to_stream(_backClicks, content->lifetime());
_topBarColor = content->edgeColor();
return base::make_weak(not_null<Ui::RpWidget*>{ content });
}

View File

@@ -57,6 +57,8 @@ public:
int addingToAlbumId = 0);
~InnerWidget();
[[nodiscard]] rpl::producer<> backRequest() const;
bool showInternal(not_null<Memento*> memento);
void setIsStackBottom(bool isStackBottom) {
_isStackBottom = isStackBottom;
@@ -162,6 +164,7 @@ private:
rpl::variable<bool> _albumEmpty;
rpl::variable<bool> _backToggles;
rpl::event_stream<> _backClicks;
rpl::event_stream<> _showFinished;
rpl::variable<std::optional<QColor>> _topBarColor;

View File

@@ -124,6 +124,10 @@ Widget::Widget(
) | rpl::start_with_next([=] {
refreshBottom();
}, _inner->lifetime());
_inner->backRequest() | rpl::start_with_next([=] {
checkBeforeClose([=] { controller->showBackFromStack(); });
}, _inner->lifetime());
}
void Widget::setInnerFocus() {

View File

@@ -194,8 +194,7 @@ void PreparedPreviewBox(
container,
tr::lng_bot_share_prepared_about(lt_bot, rpl::single(name)),
st::boxDividerLabel),
st::defaultBoxDividerLabelPadding,
RectPart::Top | RectPart::Bottom)));
st::defaultBoxDividerLabelPadding)));
const auto row = container->add(object_ptr<Ui::VerticalLayout>(
container));

View File

@@ -31,10 +31,6 @@ public:
not_null<Main::Account*> account,
not_null<Data*> data);
QAccessible::Role accessibilityRole() override {
return QAccessible::Role::Dialog;
}
bool hasBack() const override {
return true;
}

View File

@@ -166,6 +166,10 @@ QString CodeInput::accessibilityName() {
return tr::lng_code_ph(tr::now);
}
QString CodeInput::accessibilityValue() const {
return collectDigits();
}
void CodeInput::setDigitsCountMax(int digitsCount) {
_digitsCountMax = digitsCount;
@@ -315,6 +319,7 @@ void CodeInput::insertCodeAndSubmit(const QString &code) {
&& _digits[_currentIndex]->digit() != kDigitNone) {
requestCode();
}
accessibilityValueChanged();
}
QString CodeInput::collectDigits() const {

View File

@@ -22,6 +22,7 @@ public:
return QAccessible::EditableText;
}
QString accessibilityName() override;
QString accessibilityValue() const override;
void setDigitsCountMax(int digitsCount);

View File

@@ -29,10 +29,6 @@ public:
not_null<Main::Account*> account,
not_null<Data*> data);
QAccessible::Role accessibilityRole() override {
return QAccessible::Role::Dialog;
}
void setInnerFocus() override;
void activate() override;
void cancelled() override;

View File

@@ -28,9 +28,6 @@ public:
not_null<Main::Account*> account,
not_null<Data*> data);
QAccessible::Role accessibilityRole() override {
return QAccessible::Role::Dialog;
}
QString accessibilityName() override;
void selectCountry(const QString &country);

View File

@@ -25,9 +25,6 @@ public:
not_null<Main::Account*> account,
not_null<Data*> data);
QAccessible::Role accessibilityRole() override {
return QAccessible::Role::Dialog;
}
QString accessibilityName() override;
QString accessibilityDescription() override;

View File

@@ -38,5 +38,18 @@ rpl::producer<QString> StartWidget::nextButtonText() const {
return tr::lng_start_msgs();
}
rpl::producer<> StartWidget::nextButtonFocusRequests() const {
return _nextButtonFocusRequests.events();
}
void StartWidget::activate() {
Step::activate();
setInnerFocus();
}
void StartWidget::setInnerFocus() {
_nextButtonFocusRequests.fire({});
}
} // namespace details
} // namespace Intro

View File

@@ -27,6 +27,12 @@ public:
void submit() override;
rpl::producer<QString> nextButtonText() const override;
rpl::producer<> nextButtonFocusRequests() const override;
void activate() override;
void setInnerFocus() override;
private:
rpl::event_stream<> _nextButtonFocusRequests;
};

View File

@@ -133,6 +133,10 @@ rpl::producer<const style::RoundButton*> Step::nextButtonStyle() const {
return rpl::single((const style::RoundButton*)(nullptr));
}
rpl::producer<> Step::nextButtonFocusRequests() const {
return rpl::never();
}
void Step::goBack() {
if (_goCallback) {
_goCallback(nullptr, StackAction::Back, Animate::Back);

View File

@@ -46,7 +46,7 @@ public:
~Step();
QAccessible::Role accessibilityRole() override {
return QAccessible::Role::Pane;
return QAccessible::Role::Dialog;
}
QString accessibilityName() override {
return _titleText.current();
@@ -94,6 +94,7 @@ public:
[[nodiscard]] virtual rpl::producer<QString> nextButtonText() const;
[[nodiscard]] virtual auto nextButtonStyle() const
-> rpl::producer<const style::RoundButton*>;
[[nodiscard]] virtual rpl::producer<> nextButtonFocusRequests() const;
[[nodiscard]] int contentLeft() const;
[[nodiscard]] int contentTop() const;

View File

@@ -116,6 +116,7 @@ Widget::Widget(
default: Unexpected("Enter point in Intro::Widget::Widget.");
}
setupStep();
fixOrder();
if (_account->mtp().isTestMode()) {
@@ -342,6 +343,32 @@ void Widget::setInnerFocus() {
}
}
void Widget::setupStep() {
getStep()->nextButtonStyle(
) | rpl::start_with_next([=](const style::RoundButton *st) {
const auto nextStyle = st ? st : &st::introNextButton;
if (_nextStyle != nextStyle) {
_nextStyle = nextStyle;
const auto wasShown = _next->toggled();
_next.destroy();
_next.create(
this,
object_ptr<Ui::RoundButton>(this, nullptr, *nextStyle));
showControls();
updateControlsGeometry();
_next->toggle(wasShown, anim::type::instant);
}
}, getStep()->lifetime());
getStep()->nextButtonFocusRequests() | rpl::start_with_next([=] {
if (_next && !_next->isHidden()) {
_next->entity()->setFocus(Qt::OtherFocusReason);
}
}, getStep()->lifetime());
getStep()->finishInit();
}
void Widget::historyMove(StackAction action, Animate animate) {
Expects(_stepHistory.size() > 1);
@@ -363,25 +390,8 @@ void Widget::historyMove(StackAction action, Animate animate) {
if (_terms) {
hideAndDestroy(std::exchange(_terms, { nullptr }));
}
{
getStep()->nextButtonStyle(
) | rpl::start_with_next([=](const style::RoundButton *st) {
const auto nextStyle = st ? st : &st::introNextButton;
if (_nextStyle != nextStyle) {
_nextStyle = nextStyle;
const auto wasShown = _next->toggled();
_next.destroy();
_next.create(
this,
object_ptr<Ui::RoundButton>(this, nullptr, *nextStyle));
showControls();
updateControlsGeometry();
_next->toggle(wasShown, anim::type::instant);
}
}, _next->lifetime());
}
setupStep();
getStep()->finishInit();
getStep()->prepareShowAnimated(wasStep);
if (wasStep->hasCover() != getStep()->hasCover()) {
_nextTopFrom = wasStep->contentTop() + st::introNextTop;

View File

@@ -120,6 +120,7 @@ protected:
void keyPressEvent(QKeyEvent *e) override;
private:
void setupStep();
void refreshLang();
void showFinished();
void createLanguageLink();

View File

@@ -151,6 +151,8 @@ void MainWindow::finishFirstShow() {
if (!_passcodeLock && _main) {
_main->activate();
} else if (!_passcodeLock && _intro) {
_intro->setInnerFocus();
}
}

Some files were not shown because too many files have changed in this diff Show More