Compare commits
100 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fea6393c6 | ||
|
|
53f063fe16 | ||
|
|
6ee72b9c32 | ||
|
|
5faefa7997 | ||
|
|
b0726cd31a | ||
|
|
64f5fa8dc3 | ||
|
|
ad300f5eae | ||
|
|
7331160e22 | ||
|
|
6af82e5ae5 | ||
|
|
9b2347171f | ||
|
|
c60629c17d | ||
|
|
353e18e8e3 | ||
|
|
f069df285d | ||
|
|
6c5f8dffa6 | ||
|
|
4a77e23b54 | ||
|
|
942e53b59b | ||
|
|
21a6a256b9 | ||
|
|
99567d3a53 | ||
|
|
eddad6b690 | ||
|
|
d02a8cbca1 | ||
|
|
3b079aa29a | ||
|
|
df5307dd32 | ||
|
|
65891508e0 | ||
|
|
b5f4e40c3e | ||
|
|
a427730acd | ||
|
|
d7639a1ab6 | ||
|
|
01a140ea29 | ||
|
|
6e0ff9b6a0 | ||
|
|
fb14eeeb1e | ||
|
|
0c701d9d95 | ||
|
|
f1ceb1c95a | ||
|
|
6bd41a07a1 | ||
|
|
2b78bb6e79 | ||
|
|
7e5d5ddafe | ||
|
|
150c25c81a | ||
|
|
95319bdaca | ||
|
|
d982835b50 | ||
|
|
6f040aa0b5 | ||
|
|
080ecece66 | ||
|
|
31ece2c26e | ||
|
|
9a20f4b935 | ||
|
|
ea61fd22f5 | ||
|
|
c77f8f9f41 | ||
|
|
ad758c51c2 | ||
|
|
8ac7fd14ec | ||
|
|
ed7c5e97cb | ||
|
|
fb928a15a6 | ||
|
|
f60155e7b8 | ||
|
|
7c28b1a6a6 | ||
|
|
a300f4662e | ||
|
|
3ff376774f | ||
|
|
e16c05385f | ||
|
|
6e8825cdd5 | ||
|
|
1c19895ce7 | ||
|
|
e62a4b065a | ||
|
|
ba363285a7 | ||
|
|
679c932697 | ||
|
|
581ec70bf3 | ||
|
|
4d2700ab1c | ||
|
|
b365136639 | ||
|
|
f23153e1ac | ||
|
|
26cb931dcf | ||
|
|
42d2190d17 | ||
|
|
63ad80200e | ||
|
|
68e9c63693 | ||
|
|
542ff88d3b | ||
|
|
bf7f73e472 | ||
|
|
c9f195be90 | ||
|
|
715da30a72 | ||
|
|
4e9f2aaadd | ||
|
|
71cbb037ce | ||
|
|
9e43972313 | ||
|
|
4a8bb75851 | ||
|
|
80dce3b65a | ||
|
|
056534fb41 | ||
|
|
e444b82683 | ||
|
|
d9e4f686fb | ||
|
|
2cd224af98 | ||
|
|
a50141ac8a | ||
|
|
f11b36cdb1 | ||
|
|
f30af1c4ed | ||
|
|
45def31826 | ||
|
|
a22afce820 | ||
|
|
af874bebfd | ||
|
|
7cdb651538 | ||
|
|
5e1752bcbc | ||
|
|
eb295cb19c | ||
|
|
ace42226b6 | ||
|
|
5e1b4b4e6a | ||
|
|
f009fa9e47 | ||
|
|
f08c00557d | ||
|
|
a0e0d95775 | ||
|
|
a4acab983d | ||
|
|
5237a7977d | ||
|
|
eb285bc1ac | ||
|
|
7d97cd25ab | ||
|
|
55f4a99824 | ||
|
|
34373836b9 | ||
|
|
1509891ec0 | ||
|
|
877ef7d78f |
2
.github/workflows/linux.yml
vendored
@@ -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:
|
||||
|
||||
2
.github/workflows/mac.yml
vendored
@@ -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:
|
||||
|
||||
2
.github/workflows/mac_packaged.yml
vendored
@@ -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:
|
||||
|
||||
2
.github/workflows/snap.yml
vendored
@@ -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:
|
||||
|
||||
2
.github/workflows/win.yml
vendored
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
BIN
Telegram/Resources/animations/rtmp.tgs
Normal file
BIN
Telegram/Resources/animations/show_or_premium_lastseen.tgs
Normal file
BIN
Telegram/Resources/animations/show_or_premium_readtime.tgs
Normal file
BIN
Telegram/Resources/animations/toast/chats_filter_in.tgs
Normal file
BIN
Telegram/Resources/icons/profile/profile_manage.png
Normal file
|
After Width: | Height: | Size: 606 B |
BIN
Telegram/Resources/icons/profile/profile_manage@2x.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/profile/profile_manage@3x.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 866 B |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 764 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
@@ -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 |
@@ -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**.";
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -111,6 +111,10 @@ void ChatCreateDone(
|
||||
show,
|
||||
chat,
|
||||
CollectForbiddenUsers(&chat->session(), result));
|
||||
chat->owner().addRecentJoinChat({
|
||||
.fromPeerId = chat->id,
|
||||
.joinedPeerId = chat->id,
|
||||
});
|
||||
}
|
||||
};
|
||||
if (!success) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1070,6 +1070,10 @@ void AddParticipantsBoxController::Start(
|
||||
channel,
|
||||
params,
|
||||
ShowAtTheEndMsgId);
|
||||
channel->owner().addRecentJoinChat({
|
||||
.fromPeerId = channel->id,
|
||||
.joinedPeerId = channel->id,
|
||||
});
|
||||
}
|
||||
}, box->lifetime());
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -285,7 +285,7 @@ void SettingsBox(
|
||||
layout->add(object_ptr<Ui::BoxContentDivider>(
|
||||
layout,
|
||||
st::boxDividerHeight,
|
||||
st::groupCallDividerBg));
|
||||
st::groupCallDividerBar));
|
||||
};
|
||||
|
||||
if (addCheck || addMessages) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -68,6 +68,7 @@ enum class StickerLottieSize : uint8 {
|
||||
EmojiInteractionReserved7,
|
||||
ChatIntroHelloSticker,
|
||||
StickerEmojiSize,
|
||||
PinnedProfileUniqueGiftSize,
|
||||
};
|
||||
[[nodiscard]] uint8 LottieCacheKeyShift(
|
||||
uint8 replacementsTag,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ struct PremiumSubscriptionOption {
|
||||
QString discount;
|
||||
QString costPerMonth;
|
||||
QString costNoDiscount;
|
||||
QString costTotal;
|
||||
QString costPerYear;
|
||||
QString currency;
|
||||
QString total;
|
||||
QString botUrl;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)] {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
}));
|
||||
|
||||
@@ -213,7 +213,7 @@ void AddPremiumTopBarWithDefaultTitleBar(
|
||||
- st::boxTitleHeight
|
||||
+ st::boxDividerHeight
|
||||
+ st::defaultVerticalListSkip,
|
||||
st::boxDividerBg,
|
||||
st::defaultDividerBar,
|
||||
RectPart::Bottom),
|
||||
style::margins());
|
||||
bar->setPaused(true);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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(
|
||||
|
||||
@@ -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> ÷rOverridden);
|
||||
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> ÷rOverridden);
|
||||
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> ÷rOverridden);
|
||||
|
||||
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> ÷rOverridden)
|
||||
: _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> ÷rOverridden)
|
||||
: _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> ÷rOverridden)
|
||||
: _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> ÷rOverridden) {
|
||||
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> ÷rOverridden) {
|
||||
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> ÷rOverridden) {
|
||||
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> ÷rOverridden) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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> ÷rOverridden);
|
||||
|
||||
} // namespace Info::Profile
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
83
Telegram/SourceFiles/info/saved/info_saved_music_common.cpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
(
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -22,6 +22,7 @@ public:
|
||||
return QAccessible::EditableText;
|
||||
}
|
||||
QString accessibilityName() override;
|
||||
QString accessibilityValue() const override;
|
||||
|
||||
void setDigitsCountMax(int digitsCount);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -120,6 +120,7 @@ protected:
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
|
||||
private:
|
||||
void setupStep();
|
||||
void refreshLang();
|
||||
void showFinished();
|
||||
void createLanguageLink();
|
||||
|
||||
@@ -151,6 +151,8 @@ void MainWindow::finishFirstShow() {
|
||||
|
||||
if (!_passcodeLock && _main) {
|
||||
_main->activate();
|
||||
} else if (!_passcodeLock && _intro) {
|
||||
_intro->setInnerFocus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||