Merge tag 'v5.5.3' into dev
# Conflicts: # Telegram/Resources/winrc/Telegram.rc # Telegram/Resources/winrc/Updater.rc # Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp # Telegram/SourceFiles/core/version.h # Telegram/SourceFiles/history/history_widget.cpp # Telegram/SourceFiles/history/view/history_view_element.cpp # Telegram/lib_tl # Telegram/lib_ui # lib/xdg/org.telegram.desktop.metainfo.xml # snap/snapcraft.yaml
|
@ -37,6 +37,10 @@ include(cmake/td_scheme.cmake)
|
||||||
include(cmake/td_ui.cmake)
|
include(cmake/td_ui.cmake)
|
||||||
include(cmake/generate_appdata_changelog.cmake)
|
include(cmake/generate_appdata_changelog.cmake)
|
||||||
|
|
||||||
|
if (DESKTOP_APP_TEST_APPS)
|
||||||
|
include(cmake/tests.cmake)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
include(cmake/generate_midl.cmake)
|
include(cmake/generate_midl.cmake)
|
||||||
generate_midl(Telegram ${src_loc}
|
generate_midl(Telegram ${src_loc}
|
||||||
|
@ -390,6 +394,8 @@ PRIVATE
|
||||||
boxes/self_destruction_box.h
|
boxes/self_destruction_box.h
|
||||||
boxes/send_credits_box.cpp
|
boxes/send_credits_box.cpp
|
||||||
boxes/send_credits_box.h
|
boxes/send_credits_box.h
|
||||||
|
boxes/send_gif_with_caption_box.cpp
|
||||||
|
boxes/send_gif_with_caption_box.h
|
||||||
boxes/send_files_box.cpp
|
boxes/send_files_box.cpp
|
||||||
boxes/send_files_box.h
|
boxes/send_files_box.h
|
||||||
boxes/sessions_box.cpp
|
boxes/sessions_box.cpp
|
||||||
|
@ -1556,6 +1562,8 @@ PRIVATE
|
||||||
support/support_templates.h
|
support/support_templates.h
|
||||||
ui/boxes/edit_invite_link_session.cpp
|
ui/boxes/edit_invite_link_session.cpp
|
||||||
ui/boxes/edit_invite_link_session.h
|
ui/boxes/edit_invite_link_session.h
|
||||||
|
ui/boxes/peer_qr_box.cpp
|
||||||
|
ui/boxes/peer_qr_box.h
|
||||||
ui/chat/attach/attach_item_single_file_preview.cpp
|
ui/chat/attach/attach_item_single_file_preview.cpp
|
||||||
ui/chat/attach/attach_item_single_file_preview.h
|
ui/chat/attach/attach_item_single_file_preview.h
|
||||||
ui/chat/attach/attach_item_single_media_preview.cpp
|
ui/chat/attach/attach_item_single_media_preview.cpp
|
||||||
|
@ -1564,6 +1572,8 @@ PRIVATE
|
||||||
ui/chat/choose_send_as.h
|
ui/chat/choose_send_as.h
|
||||||
ui/chat/choose_theme_controller.cpp
|
ui/chat/choose_theme_controller.cpp
|
||||||
ui/chat/choose_theme_controller.h
|
ui/chat/choose_theme_controller.h
|
||||||
|
ui/controls/emoji_button_factory.cpp
|
||||||
|
ui/controls/emoji_button_factory.h
|
||||||
ui/controls/location_picker.cpp
|
ui/controls/location_picker.cpp
|
||||||
ui/controls/location_picker.h
|
ui/controls/location_picker.h
|
||||||
ui/controls/silent_toggle.cpp
|
ui/controls/silent_toggle.cpp
|
||||||
|
@ -1589,6 +1599,8 @@ PRIVATE
|
||||||
ui/image/image_location_factory.h
|
ui/image/image_location_factory.h
|
||||||
ui/text/format_song_document_name.cpp
|
ui/text/format_song_document_name.cpp
|
||||||
ui/text/format_song_document_name.h
|
ui/text/format_song_document_name.h
|
||||||
|
ui/widgets/expandable_peer_list.cpp
|
||||||
|
ui/widgets/expandable_peer_list.h
|
||||||
ui/widgets/label_with_custom_emoji.cpp
|
ui/widgets/label_with_custom_emoji.cpp
|
||||||
ui/widgets/label_with_custom_emoji.h
|
ui/widgets/label_with_custom_emoji.h
|
||||||
ui/countryinput.cpp
|
ui/countryinput.cpp
|
||||||
|
|
Before Width: | Height: | Size: 214 B After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 426 B After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 912 B After Width: | Height: | Size: 1.6 KiB |
BIN
Telegram/Resources/icons/calls/mini_calls_arrow.png
Normal file
After Width: | Height: | Size: 240 B |
BIN
Telegram/Resources/icons/calls/mini_calls_arrow@2x.png
Normal file
After Width: | Height: | Size: 324 B |
BIN
Telegram/Resources/icons/calls/mini_calls_arrow@3x.png
Normal file
After Width: | Height: | Size: 422 B |
1
Telegram/Resources/icons/plane_white.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg width="1000px" height="1000px" viewBox="0 0 1000 1000" version="1.1" xmlns="http://www.w3.org/2000/svg"><g id="Artboard" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><path d="M226.328419,494.722069 C372.088573,431.216685 469.284839,389.350049 517.917216,369.122161 C656.772535,311.36743 685.625481,301.334815 704.431427,301.003532 C708.567621,300.93067 717.815839,301.955743 723.806446,306.816707 C728.864797,310.92121 730.256552,316.46581 730.922551,320.357329 C731.588551,324.248848 732.417879,333.113828 731.758626,340.040666 C724.234007,419.102486 691.675104,610.964674 675.110982,699.515267 C668.10208,736.984342 654.301336,749.547532 640.940618,750.777006 C611.904684,753.448938 589.856115,731.588035 561.733393,713.153237 C517.726886,684.306416 492.866009,666.349181 450.150074,638.200013 C400.78442,605.66878 432.786119,587.789048 460.919462,558.568563 C468.282091,550.921423 596.21508,434.556479 598.691227,424.000355 C599.00091,422.680135 599.288312,417.758981 596.36474,415.160431 C593.441168,412.561881 589.126229,413.450484 586.012448,414.157198 C581.598758,415.158943 511.297793,461.625274 375.109553,553.556189 C355.154858,567.258623 337.080515,573.934908 320.886524,573.585046 C303.033948,573.199351 268.692754,563.490928 243.163606,555.192408 C211.851067,545.013936 186.964484,539.632504 189.131547,522.346309 C190.260287,513.342589 202.659244,504.134509 226.328419,494.722069 Z" id="Path-3" fill="#FFFFFF"></path></g></svg>
|
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/Resources/icons/qr_mini.png
Normal file
After Width: | Height: | Size: 641 B |
BIN
Telegram/Resources/icons/qr_mini@2x.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/qr_mini@3x.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
|
@ -31,6 +31,7 @@
|
||||||
<file alias="topic_icons/gray.svg">../../art/topic_icons/gray.svg</file>
|
<file alias="topic_icons/gray.svg">../../art/topic_icons/gray.svg</file>
|
||||||
<file alias="topic_icons/general.svg">../../art/topic_icons/general.svg</file>
|
<file alias="topic_icons/general.svg">../../art/topic_icons/general.svg</file>
|
||||||
<file alias="links_subscription.svg">../../icons/info/edit/links_subscription.svg</file>
|
<file alias="links_subscription.svg">../../icons/info/edit/links_subscription.svg</file>
|
||||||
|
<file alias="plane_white.svg">../../icons/plane_white.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/icons">
|
<qresource prefix="/icons">
|
||||||
<file alias="calls/hands.lottie">../../icons/calls/hands.lottie</file>
|
<file alias="calls/hands.lottie">../../icons/calls/hands.lottie</file>
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||||
ProcessorArchitecture="ARCHITECTURE"
|
ProcessorArchitecture="ARCHITECTURE"
|
||||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||||
Version="5.4.1.0" />
|
Version="5.5.3.0" />
|
||||||
<Properties>
|
<Properties>
|
||||||
<DisplayName>Telegram Desktop</DisplayName>
|
<DisplayName>Telegram Desktop</DisplayName>
|
||||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||||
|
|
|
@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 5,4,1,0
|
FILEVERSION 5,5,3,0
|
||||||
PRODUCTVERSION 5,4,1,0
|
PRODUCTVERSION 5,5,3,0
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
@ -62,10 +62,10 @@ BEGIN
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "Radolyn Labs"
|
VALUE "CompanyName", "Radolyn Labs"
|
||||||
VALUE "FileDescription", "AyuGram Desktop"
|
VALUE "FileDescription", "AyuGram Desktop"
|
||||||
VALUE "FileVersion", "5.4.1.0"
|
VALUE "FileVersion", "5.5.3.0"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
||||||
VALUE "ProductName", "AyuGram Desktop"
|
VALUE "ProductName", "AyuGram Desktop"
|
||||||
VALUE "ProductVersion", "5.4.1.0"
|
VALUE "ProductVersion", "5.5.3.0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|
|
@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 5,4,1,0
|
FILEVERSION 5,5,3,0
|
||||||
PRODUCTVERSION 5,4,1,0
|
PRODUCTVERSION 5,5,3,0
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
@ -53,10 +53,10 @@ BEGIN
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "Radolyn Labs"
|
VALUE "CompanyName", "Radolyn Labs"
|
||||||
VALUE "FileDescription", "AyuGram Desktop Updater"
|
VALUE "FileDescription", "AyuGram Desktop Updater"
|
||||||
VALUE "FileVersion", "5.4.1.0"
|
VALUE "FileVersion", "5.5.3.0"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
||||||
VALUE "ProductName", "AyuGram Desktop"
|
VALUE "ProductName", "AyuGram Desktop"
|
||||||
VALUE "ProductVersion", "5.4.1.0"
|
VALUE "ProductVersion", "5.5.3.0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|
|
@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "boxes/url_auth_box.h"
|
#include "boxes/url_auth_box.h"
|
||||||
#include "boxes/peers/choose_peer_box.h"
|
#include "boxes/peers/choose_peer_box.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
#include "chat_helpers/bot_command.h"
|
||||||
#include "core/core_cloud_password.h"
|
#include "core/core_cloud_password.h"
|
||||||
#include "core/click_handler_types.h"
|
#include "core/click_handler_types.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
|
|
|
@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/filter_icons.h"
|
#include "ui/filter_icons.h"
|
||||||
#include "ui/vertical_list.h"
|
#include "ui/vertical_list.h"
|
||||||
|
#include "ui/ui_utility.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "styles/style_filter_icons.h"
|
#include "styles/style_filter_icons.h"
|
||||||
#include "styles/style_layers.h"
|
#include "styles/style_layers.h"
|
||||||
|
@ -231,12 +232,12 @@ void ImportInvite(
|
||||||
api->request(MTPchatlists_JoinChatlistInvite(
|
api->request(MTPchatlists_JoinChatlistInvite(
|
||||||
MTP_string(slug),
|
MTP_string(slug),
|
||||||
MTP_vector<MTPInputPeer>(std::move(inputs))
|
MTP_vector<MTPInputPeer>(std::move(inputs))
|
||||||
)).done(callback).fail(error).send();
|
)).done(callback).fail(error).handleFloodErrors().send();
|
||||||
} else {
|
} else {
|
||||||
api->request(MTPchatlists_JoinChatlistUpdates(
|
api->request(MTPchatlists_JoinChatlistUpdates(
|
||||||
MTP_inputChatlistDialogFilter(MTP_int(filterId)),
|
MTP_inputChatlistDialogFilter(MTP_int(filterId)),
|
||||||
MTP_vector<MTPInputPeer>(std::move(inputs))
|
MTP_vector<MTPInputPeer>(std::move(inputs))
|
||||||
)).done(callback).fail(error).send();
|
)).done(callback).fail(error).handleFloodErrors().send();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -516,6 +517,8 @@ void ShowImportError(
|
||||||
} else {
|
} else {
|
||||||
window->showToast((error == u"INVITE_SLUG_EXPIRED"_q)
|
window->showToast((error == u"INVITE_SLUG_EXPIRED"_q)
|
||||||
? tr::lng_group_invite_bad_link(tr::now)
|
? tr::lng_group_invite_bad_link(tr::now)
|
||||||
|
: error.startsWith(u"FLOOD_WAIT_"_q)
|
||||||
|
? tr::lng_flood_error(tr::now)
|
||||||
: error);
|
: error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -367,12 +367,15 @@ void CheckChatInvite(
|
||||||
result.match([=](const MTPDchatInvite &data) {
|
result.match([=](const MTPDchatInvite &data) {
|
||||||
const auto isGroup = !data.is_broadcast();
|
const auto isGroup = !data.is_broadcast();
|
||||||
const auto hasPricing = !!data.vsubscription_pricing();
|
const auto hasPricing = !!data.vsubscription_pricing();
|
||||||
if (hasPricing && !data.vsubscription_form_id()) {
|
const auto canRefulfill = data.is_can_refulfill_subscription();
|
||||||
|
if (hasPricing
|
||||||
|
&& !canRefulfill
|
||||||
|
&& !data.vsubscription_form_id()) {
|
||||||
strong->uiShow()->showToast(
|
strong->uiShow()->showToast(
|
||||||
tr::lng_confirm_phone_link_invalid(tr::now));
|
tr::lng_confirm_phone_link_invalid(tr::now));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto box = hasPricing
|
const auto box = (hasPricing && !canRefulfill)
|
||||||
? strong->show(Box(
|
? strong->show(Box(
|
||||||
ConfirmSubscriptionBox,
|
ConfirmSubscriptionBox,
|
||||||
session,
|
session,
|
||||||
|
|
|
@ -264,14 +264,24 @@ ChatParticipant::ChatParticipant(
|
||||||
_rank = qs(data.vrank().value_or_empty());
|
_rank = qs(data.vrank().value_or_empty());
|
||||||
_rights = ChatAdminRightsInfo(data.vadmin_rights());
|
_rights = ChatAdminRightsInfo(data.vadmin_rights());
|
||||||
_by = peerToUser(peerFromUser(data.vpromoted_by()));
|
_by = peerToUser(peerFromUser(data.vpromoted_by()));
|
||||||
|
_date = data.vdate().v;
|
||||||
}, [&](const MTPDchannelParticipantSelf &data) {
|
}, [&](const MTPDchannelParticipantSelf &data) {
|
||||||
_type = Type::Member;
|
_type = Type::Member;
|
||||||
|
_date = data.vdate().v;
|
||||||
_by = peerToUser(peerFromUser(data.vinviter_id()));
|
_by = peerToUser(peerFromUser(data.vinviter_id()));
|
||||||
|
if (data.vsubscription_until_date()) {
|
||||||
|
_subscriptionDate = data.vsubscription_until_date()->v;
|
||||||
|
}
|
||||||
}, [&](const MTPDchannelParticipant &data) {
|
}, [&](const MTPDchannelParticipant &data) {
|
||||||
_type = Type::Member;
|
_type = Type::Member;
|
||||||
|
_date = data.vdate().v;
|
||||||
|
if (data.vsubscription_until_date()) {
|
||||||
|
_subscriptionDate = data.vsubscription_until_date()->v;
|
||||||
|
}
|
||||||
}, [&](const MTPDchannelParticipantBanned &data) {
|
}, [&](const MTPDchannelParticipantBanned &data) {
|
||||||
_restrictions = ChatRestrictionsInfo(data.vbanned_rights());
|
_restrictions = ChatRestrictionsInfo(data.vbanned_rights());
|
||||||
_by = peerToUser(peerFromUser(data.vkicked_by()));
|
_by = peerToUser(peerFromUser(data.vkicked_by()));
|
||||||
|
_date = data.vdate().v;
|
||||||
|
|
||||||
_type = (_restrictions.flags & ChatRestriction::ViewMessages)
|
_type = (_restrictions.flags & ChatRestriction::ViewMessages)
|
||||||
? Type::Banned
|
? Type::Banned
|
||||||
|
@ -348,6 +358,24 @@ ChatAdminRightsInfo ChatParticipant::rights() const {
|
||||||
return _rights;
|
return _rights;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimeId ChatParticipant::subscriptionDate() const {
|
||||||
|
return _subscriptionDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeId ChatParticipant::promotedSince() const {
|
||||||
|
return (_type == Type::Admin) ? _date : TimeId(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeId ChatParticipant::restrictedSince() const {
|
||||||
|
return (_type == Type::Restricted || _type == Type::Banned)
|
||||||
|
? _date
|
||||||
|
: TimeId(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeId ChatParticipant::memberSince() const {
|
||||||
|
return (_type == Type::Member) ? _date : TimeId(0);
|
||||||
|
}
|
||||||
|
|
||||||
ChatParticipant::Type ChatParticipant::type() const {
|
ChatParticipant::Type ChatParticipant::type() const {
|
||||||
return _type;
|
return _type;
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,11 @@ public:
|
||||||
ChatRestrictionsInfo restrictions() const;
|
ChatRestrictionsInfo restrictions() const;
|
||||||
ChatAdminRightsInfo rights() const;
|
ChatAdminRightsInfo rights() const;
|
||||||
|
|
||||||
|
TimeId subscriptionDate() const;
|
||||||
|
TimeId promotedSince() const;
|
||||||
|
TimeId restrictedSince() const;
|
||||||
|
TimeId memberSince() const;
|
||||||
|
|
||||||
Type type() const;
|
Type type() const;
|
||||||
QString rank() const;
|
QString rank() const;
|
||||||
|
|
||||||
|
@ -73,6 +78,8 @@ private:
|
||||||
bool _canBeEdited = false;
|
bool _canBeEdited = false;
|
||||||
|
|
||||||
QString _rank;
|
QString _rank;
|
||||||
|
TimeId _subscriptionDate = 0;
|
||||||
|
TimeId _date = 0;
|
||||||
|
|
||||||
ChatRestrictionsInfo _restrictions;
|
ChatRestrictionsInfo _restrictions;
|
||||||
ChatAdminRightsInfo _rights;
|
ChatAdminRightsInfo _rights;
|
||||||
|
|
|
@ -12,6 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "core/core_cloud_password.h"
|
#include "core/core_cloud_password.h"
|
||||||
#include "passport/passport_encryption.h"
|
#include "passport/passport_encryption.h"
|
||||||
|
|
||||||
|
#include "base/unixtime.h"
|
||||||
|
#include "base/call_delayed.h"
|
||||||
|
|
||||||
namespace Api {
|
namespace Api {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,8 @@ constexpr auto kTransactionsLimit = 100;
|
||||||
.credits = tl.data().vstars().v,
|
.credits = tl.data().vstars().v,
|
||||||
.bareMsgId = uint64(tl.data().vmsg_id().value_or_empty()),
|
.bareMsgId = uint64(tl.data().vmsg_id().value_or_empty()),
|
||||||
.barePeerId = barePeerId,
|
.barePeerId = barePeerId,
|
||||||
|
.bareGiveawayMsgId = uint64(
|
||||||
|
tl.data().vgiveaway_post_id().value_or_empty()),
|
||||||
.peerType = tl.data().vpeer().match([](const HistoryPeerTL &) {
|
.peerType = tl.data().vpeer().match([](const HistoryPeerTL &) {
|
||||||
return Data::CreditsHistoryEntry::PeerType::Peer;
|
return Data::CreditsHistoryEntry::PeerType::Peer;
|
||||||
}, [](const MTPDstarsTransactionPeerPlayMarket &) {
|
}, [](const MTPDstarsTransactionPeerPlayMarket &) {
|
||||||
|
@ -215,6 +217,10 @@ rpl::producer<rpl::no_value, QString> CreditsTopupOptions::request() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Data::CreditTopupOptions CreditsTopupOptions::options() const {
|
||||||
|
return _options;
|
||||||
|
}
|
||||||
|
|
||||||
CreditsStatus::CreditsStatus(not_null<PeerData*> peer)
|
CreditsStatus::CreditsStatus(not_null<PeerData*> peer)
|
||||||
: _peer(peer)
|
: _peer(peer)
|
||||||
, _api(&peer->session().api().instance()) {
|
, _api(&peer->session().api().instance()) {
|
||||||
|
@ -294,10 +300,6 @@ void CreditsHistory::requestSubscriptions(
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
Data::CreditTopupOptions CreditsTopupOptions::options() const {
|
|
||||||
return _options;
|
|
||||||
}
|
|
||||||
|
|
||||||
rpl::producer<not_null<PeerData*>> PremiumPeerBot(
|
rpl::producer<not_null<PeerData*>> PremiumPeerBot(
|
||||||
not_null<Main::Session*> session) {
|
not_null<Main::Session*> session) {
|
||||||
const auto username = session->appConfig().get<QString>(
|
const auto username = session->appConfig().get<QString>(
|
||||||
|
@ -385,4 +387,58 @@ Data::CreditsEarnStatistics CreditsEarnStatistics::data() const {
|
||||||
return _data;
|
return _data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CreditsGiveawayOptions::CreditsGiveawayOptions(not_null<PeerData*> peer)
|
||||||
|
: _peer(peer)
|
||||||
|
, _api(&peer->session().api().instance()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<rpl::no_value, QString> CreditsGiveawayOptions::request() {
|
||||||
|
return [=](auto consumer) {
|
||||||
|
auto lifetime = rpl::lifetime();
|
||||||
|
|
||||||
|
using TLOption = MTPStarsGiveawayOption;
|
||||||
|
|
||||||
|
const auto optionsFromTL = [=](const auto &options) {
|
||||||
|
return ranges::views::all(
|
||||||
|
options
|
||||||
|
) | ranges::views::transform([=](const auto &option) {
|
||||||
|
return Data::CreditsGiveawayOption{
|
||||||
|
.winners = ranges::views::all(
|
||||||
|
option.data().vwinners().v
|
||||||
|
) | ranges::views::transform([](const auto &winner) {
|
||||||
|
return Data::CreditsGiveawayOption::Winner{
|
||||||
|
.users = winner.data().vusers().v,
|
||||||
|
.perUserStars = winner.data().vper_user_stars().v,
|
||||||
|
.isDefault = winner.data().is_default(),
|
||||||
|
};
|
||||||
|
}) | ranges::to_vector,
|
||||||
|
.storeProduct = qs(
|
||||||
|
option.data().vstore_product().value_or_empty()),
|
||||||
|
.currency = qs(option.data().vcurrency()),
|
||||||
|
.amount = option.data().vamount().v,
|
||||||
|
.credits = option.data().vstars().v,
|
||||||
|
.yearlyBoosts = option.data().vyearly_boosts().v,
|
||||||
|
.isExtended = option.data().is_extended(),
|
||||||
|
.isDefault = option.data().is_default(),
|
||||||
|
};
|
||||||
|
}) | ranges::to_vector;
|
||||||
|
};
|
||||||
|
const auto fail = [=](const MTP::Error &error) {
|
||||||
|
consumer.put_error_copy(error.type());
|
||||||
|
};
|
||||||
|
|
||||||
|
_api.request(MTPpayments_GetStarsGiveawayOptions(
|
||||||
|
)).done([=](const MTPVector<TLOption> &result) {
|
||||||
|
_options = optionsFromTL(result.v);
|
||||||
|
consumer.put_done();
|
||||||
|
}).fail(fail).send();
|
||||||
|
|
||||||
|
return lifetime;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Data::CreditsGiveawayOptions CreditsGiveawayOptions::options() const {
|
||||||
|
return _options;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Api
|
} // namespace Api
|
||||||
|
|
|
@ -36,6 +36,22 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CreditsGiveawayOptions final {
|
||||||
|
public:
|
||||||
|
CreditsGiveawayOptions(not_null<PeerData*> peer);
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<rpl::no_value, QString> request();
|
||||||
|
[[nodiscard]] Data::CreditsGiveawayOptions options() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const not_null<PeerData*> _peer;
|
||||||
|
|
||||||
|
Data::CreditsGiveawayOptions _options;
|
||||||
|
|
||||||
|
MTP::Sender _api;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class CreditsStatus final {
|
class CreditsStatus final {
|
||||||
public:
|
public:
|
||||||
CreditsStatus(not_null<PeerData*> peer);
|
CreditsStatus(not_null<PeerData*> peer);
|
||||||
|
|
|
@ -115,6 +115,28 @@ rpl::producer<bool> GlobalPrivacy::newRequirePremium() const {
|
||||||
return _newRequirePremium.value();
|
return _newRequirePremium.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GlobalPrivacy::loadPaidReactionAnonymous() {
|
||||||
|
if (_paidReactionAnonymousLoaded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_paidReactionAnonymousLoaded = true;
|
||||||
|
_api.request(MTPmessages_GetPaidReactionPrivacy(
|
||||||
|
)).done([=](const MTPUpdates &result) {
|
||||||
|
_session->api().applyUpdates(result);
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalPrivacy::updatePaidReactionAnonymous(bool value) {
|
||||||
|
_paidReactionAnonymous = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GlobalPrivacy::paidReactionAnonymousCurrent() const {
|
||||||
|
return _paidReactionAnonymous.current();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<bool> GlobalPrivacy::paidReactionAnonymous() const {
|
||||||
|
return _paidReactionAnonymous.value();
|
||||||
|
}
|
||||||
|
|
||||||
void GlobalPrivacy::updateArchiveAndMute(bool value) {
|
void GlobalPrivacy::updateArchiveAndMute(bool value) {
|
||||||
update(
|
update(
|
||||||
|
|
|
@ -49,6 +49,11 @@ public:
|
||||||
[[nodiscard]] bool newRequirePremiumCurrent() const;
|
[[nodiscard]] bool newRequirePremiumCurrent() const;
|
||||||
[[nodiscard]] rpl::producer<bool> newRequirePremium() const;
|
[[nodiscard]] rpl::producer<bool> newRequirePremium() const;
|
||||||
|
|
||||||
|
void loadPaidReactionAnonymous();
|
||||||
|
void updatePaidReactionAnonymous(bool value);
|
||||||
|
[[nodiscard]] bool paidReactionAnonymousCurrent() const;
|
||||||
|
[[nodiscard]] rpl::producer<bool> paidReactionAnonymous() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void apply(const MTPGlobalPrivacySettings &data);
|
void apply(const MTPGlobalPrivacySettings &data);
|
||||||
|
|
||||||
|
@ -67,7 +72,9 @@ private:
|
||||||
rpl::variable<bool> _showArchiveAndMute = false;
|
rpl::variable<bool> _showArchiveAndMute = false;
|
||||||
rpl::variable<bool> _hideReadTime = false;
|
rpl::variable<bool> _hideReadTime = false;
|
||||||
rpl::variable<bool> _newRequirePremium = false;
|
rpl::variable<bool> _newRequirePremium = false;
|
||||||
|
rpl::variable<bool> _paidReactionAnonymous = false;
|
||||||
std::vector<Fn<void()>> _callbacks;
|
std::vector<Fn<void()>> _callbacks;
|
||||||
|
bool _paidReactionAnonymousLoaded = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -361,9 +361,10 @@ void Premium::resolveGiveawayInfo(
|
||||||
? GiveawayState::Refunded
|
? GiveawayState::Refunded
|
||||||
: GiveawayState::Finished;
|
: GiveawayState::Finished;
|
||||||
info.giftCode = qs(data.vgift_code_slug().value_or_empty());
|
info.giftCode = qs(data.vgift_code_slug().value_or_empty());
|
||||||
info.activatedCount = data.vactivated_count().v;
|
info.activatedCount = data.vactivated_count().value_or_empty();
|
||||||
info.finishDate = data.vfinish_date().v;
|
info.finishDate = data.vfinish_date().v;
|
||||||
info.startDate = data.vstart_date().v;
|
info.startDate = data.vstart_date().v;
|
||||||
|
info.credits = data.vstars_prize().value_or_empty();
|
||||||
});
|
});
|
||||||
_giveawayInfoDone(std::move(info));
|
_giveawayInfoDone(std::move(info));
|
||||||
}).fail([=] {
|
}).fail([=] {
|
||||||
|
@ -508,7 +509,9 @@ rpl::producer<rpl::no_value, QString> PremiumGiftCodeOptions::applyPrepaid(
|
||||||
_api.request(MTPpayments_LaunchPrepaidGiveaway(
|
_api.request(MTPpayments_LaunchPrepaidGiveaway(
|
||||||
_peer->input,
|
_peer->input,
|
||||||
MTP_long(prepaidId),
|
MTP_long(prepaidId),
|
||||||
Payments::InvoicePremiumGiftCodeGiveawayToTL(invoice)
|
invoice.creditsAmount
|
||||||
|
? Payments::InvoiceCreditsGiveawayToTL(invoice)
|
||||||
|
: Payments::InvoicePremiumGiftCodeGiveawayToTL(invoice)
|
||||||
)).done([=](const MTPUpdates &result) {
|
)).done([=](const MTPUpdates &result) {
|
||||||
_peer->session().api().applyUpdates(result);
|
_peer->session().api().applyUpdates(result);
|
||||||
consumer.put_done();
|
consumer.put_done();
|
||||||
|
@ -537,10 +540,10 @@ Payments::InvoicePremiumGiftCode PremiumGiftCodeOptions::invoice(
|
||||||
const auto token = Token{ users, months };
|
const auto token = Token{ users, months };
|
||||||
const auto &store = _stores[token];
|
const auto &store = _stores[token];
|
||||||
return Payments::InvoicePremiumGiftCode{
|
return Payments::InvoicePremiumGiftCode{
|
||||||
.randomId = randomId,
|
|
||||||
.currency = _optionsForOnePerson.currency,
|
.currency = _optionsForOnePerson.currency,
|
||||||
.amount = store.amount,
|
|
||||||
.storeProduct = store.product,
|
.storeProduct = store.product,
|
||||||
|
.randomId = randomId,
|
||||||
|
.amount = store.amount,
|
||||||
.storeQuantity = store.quantity,
|
.storeQuantity = store.quantity,
|
||||||
.users = token.users,
|
.users = token.users,
|
||||||
.months = token.months,
|
.months = token.months,
|
||||||
|
|
|
@ -57,6 +57,7 @@ struct GiveawayInfo {
|
||||||
TimeId tooEarlyDate = 0;
|
TimeId tooEarlyDate = 0;
|
||||||
TimeId finishDate = 0;
|
TimeId finishDate = 0;
|
||||||
TimeId startDate = 0;
|
TimeId startDate = 0;
|
||||||
|
uint64 credits = 0;
|
||||||
int winnersCount = 0;
|
int winnersCount = 0;
|
||||||
int activatedCount = 0;
|
int activatedCount = 0;
|
||||||
bool participating = false;
|
bool participating = false;
|
||||||
|
|
|
@ -44,7 +44,10 @@ void InnerFillMessagePostFlags(
|
||||||
if (ShouldSendSilent(peer, options)) {
|
if (ShouldSendSilent(peer, options)) {
|
||||||
flags |= MessageFlag::Silent;
|
flags |= MessageFlag::Silent;
|
||||||
}
|
}
|
||||||
if (!peer->amAnonymous()) {
|
if (!peer->amAnonymous()
|
||||||
|
|| (!peer->isBroadcast()
|
||||||
|
&& options.sendAs
|
||||||
|
&& options.sendAs != peer)) {
|
||||||
flags |= MessageFlag::HasFromId;
|
flags |= MessageFlag::HasFromId;
|
||||||
}
|
}
|
||||||
const auto channel = peer->asBroadcast();
|
const auto channel = peer->asBroadcast();
|
||||||
|
|
|
@ -571,13 +571,22 @@ rpl::producer<rpl::no_value, QString> Boosts::request() {
|
||||||
_boostStatus.prepaidGiveaway = ranges::views::all(
|
_boostStatus.prepaidGiveaway = ranges::views::all(
|
||||||
data.vprepaid_giveaways()->v
|
data.vprepaid_giveaways()->v
|
||||||
) | ranges::views::transform([](const MTPPrepaidGiveaway &r) {
|
) | ranges::views::transform([](const MTPPrepaidGiveaway &r) {
|
||||||
return Data::BoostPrepaidGiveaway{
|
return r.match([&](const MTPDprepaidGiveaway &data) {
|
||||||
.months = r.data().vmonths().v,
|
return Data::BoostPrepaidGiveaway{
|
||||||
.id = r.data().vid().v,
|
.date = base::unixtime::parse(data.vdate().v),
|
||||||
.quantity = r.data().vquantity().v,
|
.id = data.vid().v,
|
||||||
.date = QDateTime::fromSecsSinceEpoch(
|
.months = data.vmonths().v,
|
||||||
r.data().vdate().v),
|
.quantity = data.vquantity().v,
|
||||||
};
|
};
|
||||||
|
}, [&](const MTPDprepaidStarsGiveaway &data) {
|
||||||
|
return Data::BoostPrepaidGiveaway{
|
||||||
|
.date = base::unixtime::parse(data.vdate().v),
|
||||||
|
.id = data.vid().v,
|
||||||
|
.credits = data.vstars().v,
|
||||||
|
.quantity = data.vquantity().v,
|
||||||
|
.boosts = data.vboosts().v,
|
||||||
|
};
|
||||||
|
});
|
||||||
}) | ranges::to_vector;
|
}) | ranges::to_vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -635,19 +644,21 @@ void Boosts::requestBoosts(
|
||||||
}
|
}
|
||||||
: Data::GiftCodeLink();
|
: Data::GiftCodeLink();
|
||||||
list.push_back({
|
list.push_back({
|
||||||
data.is_gift(),
|
.id = qs(data.vid()),
|
||||||
data.is_giveaway(),
|
.userId = UserId(data.vuser_id().value_or_empty()),
|
||||||
data.is_unclaimed(),
|
.giveawayMessage = data.vgiveaway_msg_id()
|
||||||
qs(data.vid()),
|
|
||||||
data.vuser_id().value_or_empty(),
|
|
||||||
data.vgiveaway_msg_id()
|
|
||||||
? FullMsgId{ _peer->id, data.vgiveaway_msg_id()->v }
|
? FullMsgId{ _peer->id, data.vgiveaway_msg_id()->v }
|
||||||
: FullMsgId(),
|
: FullMsgId(),
|
||||||
QDateTime::fromSecsSinceEpoch(data.vdate().v),
|
.date = base::unixtime::parse(data.vdate().v),
|
||||||
QDateTime::fromSecsSinceEpoch(data.vexpires().v),
|
.expiresAt = base::unixtime::parse(data.vexpires().v),
|
||||||
(data.vexpires().v - data.vdate().v) / kMonthsDivider,
|
.expiresAfterMonths = ((data.vexpires().v - data.vdate().v)
|
||||||
std::move(giftCodeLink),
|
/ kMonthsDivider),
|
||||||
data.vmultiplier().value_or_empty(),
|
.giftCodeLink = std::move(giftCodeLink),
|
||||||
|
.multiplier = data.vmultiplier().value_or_empty(),
|
||||||
|
.credits = data.vstars().value_or_empty(),
|
||||||
|
.isGift = data.is_gift(),
|
||||||
|
.isGiveaway = data.is_giveaway(),
|
||||||
|
.isUnclaimed = data.is_unclaimed(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
done(Data::BoostsListSlice{
|
done(Data::BoostsListSlice{
|
||||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "api/api_authorizations.h"
|
#include "api/api_authorizations.h"
|
||||||
#include "api/api_user_names.h"
|
#include "api/api_user_names.h"
|
||||||
#include "api/api_chat_participants.h"
|
#include "api/api_chat_participants.h"
|
||||||
|
#include "api/api_global_privacy.h"
|
||||||
#include "api/api_ringtones.h"
|
#include "api/api_ringtones.h"
|
||||||
#include "api/api_text_entities.h"
|
#include "api/api_text_entities.h"
|
||||||
#include "api/api_user_privacy.h"
|
#include "api/api_user_privacy.h"
|
||||||
|
@ -2630,6 +2631,12 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||||
_session->credits().apply(data);
|
_session->credits().apply(data);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case mtpc_updatePaidReactionPrivacy: {
|
||||||
|
const auto &data = update.c_updatePaidReactionPrivacy();
|
||||||
|
_session->api().globalPrivacy().updatePaidReactionAnonymous(
|
||||||
|
mtpIsTrue(data.vprivate()));
|
||||||
|
} break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -214,7 +214,10 @@ struct State {
|
||||||
|
|
||||||
[[nodiscard]] QImage GenerateUserpic(Userpic &userpic, int size) {
|
[[nodiscard]] QImage GenerateUserpic(Userpic &userpic, int size) {
|
||||||
size *= style::DevicePixelRatio();
|
size *= style::DevicePixelRatio();
|
||||||
auto result = userpic.peer->generateUserpicImage(userpic.view, size);
|
auto result = PeerData::GenerateUserpicImage(
|
||||||
|
userpic.peer,
|
||||||
|
userpic.view,
|
||||||
|
size);
|
||||||
result.setDevicePixelRatio(style::DevicePixelRatio());
|
result.setDevicePixelRatio(style::DevicePixelRatio());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4237,8 +4237,10 @@ void ApiWrap::sendMediaWithRandomId(
|
||||||
Data::Histories::ReplyToPlaceholder(),
|
Data::Histories::ReplyToPlaceholder(),
|
||||||
(options.price
|
(options.price
|
||||||
? MTPInputMedia(MTP_inputMediaPaidMedia(
|
? MTPInputMedia(MTP_inputMediaPaidMedia(
|
||||||
|
MTP_flags(0),
|
||||||
MTP_long(options.price),
|
MTP_long(options.price),
|
||||||
MTP_vector<MTPInputMedia>(1, media)))
|
MTP_vector<MTPInputMedia>(1, media),
|
||||||
|
MTPstring()))
|
||||||
: media),
|
: media),
|
||||||
MTP_string(caption.text),
|
MTP_string(caption.text),
|
||||||
MTP_long(randomId),
|
MTP_long(randomId),
|
||||||
|
@ -4311,8 +4313,10 @@ void ApiWrap::sendMultiPaidMedia(
|
||||||
peer->input,
|
peer->input,
|
||||||
Data::Histories::ReplyToPlaceholder(),
|
Data::Histories::ReplyToPlaceholder(),
|
||||||
MTP_inputMediaPaidMedia(
|
MTP_inputMediaPaidMedia(
|
||||||
|
MTP_flags(0),
|
||||||
MTP_long(options.price),
|
MTP_long(options.price),
|
||||||
MTP_vector<MTPInputMedia>(std::move(medias))),
|
MTP_vector<MTPInputMedia>(std::move(medias)),
|
||||||
|
MTPstring()),
|
||||||
MTP_string(caption.text),
|
MTP_string(caption.text),
|
||||||
MTP_long(randomId),
|
MTP_long(randomId),
|
||||||
MTPReplyMarkup(),
|
MTPReplyMarkup(),
|
||||||
|
|
|
@ -217,7 +217,9 @@ void ShowAddParticipantsError(
|
||||||
channel,
|
channel,
|
||||||
user,
|
user,
|
||||||
ChatAdminRightsInfo(),
|
ChatAdminRightsInfo(),
|
||||||
QString());
|
QString(),
|
||||||
|
0,
|
||||||
|
nullptr);
|
||||||
box->setSaveCallback(saveCallback);
|
box->setSaveCallback(saveCallback);
|
||||||
*weak = box.data();
|
*weak = box.data();
|
||||||
show->showBox(std::move(box));
|
show->showBox(std::move(box));
|
||||||
|
|
|
@ -597,8 +597,6 @@ rightsHeaderLabel: FlatLabel(boxLabel) {
|
||||||
}
|
}
|
||||||
textFg: windowActiveTextFg;
|
textFg: windowActiveTextFg;
|
||||||
}
|
}
|
||||||
rightsUntilMargin: margins(0px, 8px, 0px, 20px);
|
|
||||||
rightsRankMargin: margins(0px, 7px, 0px, 20px);
|
|
||||||
|
|
||||||
groupStickersRemove: defaultMultiSelectSearchCancel;
|
groupStickersRemove: defaultMultiSelectSearchCancel;
|
||||||
groupStickersRemovePosition: point(6px, 6px);
|
groupStickersRemovePosition: point(6px, 6px);
|
||||||
|
@ -756,6 +754,8 @@ createPollWarningPosition: point(16px, 6px);
|
||||||
createPollCheckboxMargin: margins(22px, 10px, 22px, 10px);
|
createPollCheckboxMargin: margins(22px, 10px, 22px, 10px);
|
||||||
createPollFieldTitlePadding: margins(22px, 7px, 10px, 6px);
|
createPollFieldTitlePadding: margins(22px, 7px, 10px, 6px);
|
||||||
|
|
||||||
|
sendGifWithCaptionEmojiPosition: point(-30px, 23px);
|
||||||
|
|
||||||
backgroundCheckbox: Checkbox(defaultCheckbox) {
|
backgroundCheckbox: Checkbox(defaultCheckbox) {
|
||||||
textFg: msgServiceFg;
|
textFg: msgServiceFg;
|
||||||
textFgActive: msgServiceFg;
|
textFgActive: msgServiceFg;
|
||||||
|
@ -785,7 +785,7 @@ backgroundConfirmPadding: margins(24px, 16px, 24px, 16px);
|
||||||
backgroundConfirm: RoundButton(defaultActiveButton) {
|
backgroundConfirm: RoundButton(defaultActiveButton) {
|
||||||
height: 44px;
|
height: 44px;
|
||||||
textTop: 12px;
|
textTop: 12px;
|
||||||
font: font(13px semibold);
|
style: semiboldTextStyle;
|
||||||
}
|
}
|
||||||
backgroundConfirmCancel: RoundButton(backgroundConfirm) {
|
backgroundConfirmCancel: RoundButton(backgroundConfirm) {
|
||||||
textFg: mediaviewSaveMsgFg;
|
textFg: mediaviewSaveMsgFg;
|
||||||
|
@ -797,7 +797,7 @@ backgroundConfirmCancel: RoundButton(backgroundConfirm) {
|
||||||
|
|
||||||
height: 44px;
|
height: 44px;
|
||||||
textTop: 12px;
|
textTop: 12px;
|
||||||
font: font(13px semibold);
|
style: semiboldTextStyle;
|
||||||
|
|
||||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||||
color: shadowFg;
|
color: shadowFg;
|
||||||
|
@ -949,7 +949,7 @@ sponsoredUrlButton: RoundButton(defaultActiveButton) {
|
||||||
textFg: historyLinkInFg;
|
textFg: historyLinkInFg;
|
||||||
textFgOver: historyLinkInFg;
|
textFgOver: historyLinkInFg;
|
||||||
textTop: 7px;
|
textTop: 7px;
|
||||||
font: normalFont;
|
style: defaultTextStyle;
|
||||||
|
|
||||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||||
color: windowBgOver;
|
color: windowBgOver;
|
||||||
|
@ -1120,3 +1120,10 @@ moderateBoxDividerLabel: FlatLabel(boxDividerLabel) {
|
||||||
selectLinkFg: windowActiveTextFg;
|
selectLinkFg: windowActiveTextFg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
profileQrFont: font(fsize bold);
|
||||||
|
profileQrCenterSize: 34px;
|
||||||
|
profileQrBackgroundRadius: 12px;
|
||||||
|
profileQrIcon: icon{{ "qr_mini", windowActiveTextFg }};
|
||||||
|
profileQrBackgroundMargins: margins(36px, 12px, 36px, 12px);
|
||||||
|
profileQrBackgroundPadding: margins(0px, 24px, 0px, 24px);
|
||||||
|
|
|
@ -36,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/wrap/slide_wrap.h"
|
#include "ui/wrap/slide_wrap.h"
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
#include "ui/vertical_list.h"
|
#include "ui/vertical_list.h"
|
||||||
|
#include "ui/ui_utility.h"
|
||||||
#include "boxes/abstract_box.h" // Ui::show().
|
#include "boxes/abstract_box.h" // Ui::show().
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "styles/style_layers.h"
|
#include "styles/style_layers.h"
|
||||||
|
|
|
@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "menu/menu_send.h"
|
#include "menu/menu_send.h"
|
||||||
#include "ui/controls/emoji_button.h"
|
#include "ui/controls/emoji_button.h"
|
||||||
|
#include "ui/controls/emoji_button_factory.h"
|
||||||
#include "ui/rect.h"
|
#include "ui/rect.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/toast/toast.h"
|
#include "ui/toast/toast.h"
|
||||||
|
@ -37,6 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/wrap/fade_wrap.h"
|
#include "ui/wrap/fade_wrap.h"
|
||||||
#include "ui/wrap/slide_wrap.h"
|
#include "ui/wrap/slide_wrap.h"
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
|
#include "ui/ui_utility.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
#include "styles/style_chat_helpers.h" // defaultComposeFiles.
|
#include "styles/style_chat_helpers.h" // defaultComposeFiles.
|
||||||
|
@ -54,100 +56,6 @@ constexpr auto kSolutionLimit = 200;
|
||||||
constexpr auto kWarnSolutionLimit = 60;
|
constexpr auto kWarnSolutionLimit = 60;
|
||||||
constexpr auto kErrorLimit = 99;
|
constexpr auto kErrorLimit = 99;
|
||||||
|
|
||||||
[[nodiscard]] not_null<Ui::EmojiButton*> AddEmojiToggleToField(
|
|
||||||
not_null<Ui::InputField*> field,
|
|
||||||
not_null<Ui::BoxContent*> box,
|
|
||||||
not_null<Window::SessionController*> controller,
|
|
||||||
not_null<ChatHelpers::TabbedPanel*> emojiPanel,
|
|
||||||
QPoint shift) {
|
|
||||||
const auto emojiToggle = Ui::CreateChild<Ui::EmojiButton>(
|
|
||||||
field->parentWidget(),
|
|
||||||
st::defaultComposeFiles.emoji);
|
|
||||||
const auto fade = Ui::CreateChild<Ui::FadeAnimation>(
|
|
||||||
emojiToggle,
|
|
||||||
emojiToggle,
|
|
||||||
0.5);
|
|
||||||
{
|
|
||||||
const auto fadeTarget = Ui::CreateChild<Ui::RpWidget>(emojiToggle);
|
|
||||||
fadeTarget->resize(emojiToggle->size());
|
|
||||||
fadeTarget->paintRequest(
|
|
||||||
) | rpl::start_with_next([=](const QRect &rect) {
|
|
||||||
auto p = QPainter(fadeTarget);
|
|
||||||
if (fade->animating()) {
|
|
||||||
p.fillRect(fadeTarget->rect(), st::boxBg);
|
|
||||||
}
|
|
||||||
fade->paint(p);
|
|
||||||
}, fadeTarget->lifetime());
|
|
||||||
rpl::single(false) | rpl::then(
|
|
||||||
field->focusedChanges()
|
|
||||||
) | rpl::start_with_next([=](bool shown) {
|
|
||||||
if (shown) {
|
|
||||||
fade->fadeIn(st::universalDuration);
|
|
||||||
} else {
|
|
||||||
fade->fadeOut(st::universalDuration);
|
|
||||||
}
|
|
||||||
}, emojiToggle->lifetime());
|
|
||||||
fade->fadeOut(1);
|
|
||||||
fade->finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const auto outer = box->getDelegate()->outerContainer();
|
|
||||||
const auto allow = [](not_null<DocumentData*>) { return true; };
|
|
||||||
InitMessageFieldHandlers(
|
|
||||||
controller,
|
|
||||||
field,
|
|
||||||
Window::GifPauseReason::Layer,
|
|
||||||
allow);
|
|
||||||
Ui::Emoji::SuggestionsController::Init(
|
|
||||||
outer,
|
|
||||||
field,
|
|
||||||
&controller->session(),
|
|
||||||
Ui::Emoji::SuggestionsController::Options{
|
|
||||||
.suggestCustomEmoji = true,
|
|
||||||
.allowCustomWithoutPremium = allow,
|
|
||||||
});
|
|
||||||
const auto updateEmojiPanelGeometry = [=] {
|
|
||||||
const auto parent = emojiPanel->parentWidget();
|
|
||||||
const auto global = emojiToggle->mapToGlobal({ 0, 0 });
|
|
||||||
const auto local = parent->mapFromGlobal(global);
|
|
||||||
const auto right = local.x() + emojiToggle->width() * 3;
|
|
||||||
const auto isDropDown = local.y() < parent->height() / 2;
|
|
||||||
emojiPanel->setDropDown(isDropDown);
|
|
||||||
if (isDropDown) {
|
|
||||||
emojiPanel->moveTopRight(
|
|
||||||
local.y() + emojiToggle->height(),
|
|
||||||
right);
|
|
||||||
} else {
|
|
||||||
emojiPanel->moveBottomRight(local.y(), right);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
rpl::combine(
|
|
||||||
box->sizeValue(),
|
|
||||||
field->geometryValue()
|
|
||||||
) | rpl::start_with_next([=](QSize outer, QRect inner) {
|
|
||||||
emojiToggle->moveToLeft(
|
|
||||||
rect::right(inner) + shift.x(),
|
|
||||||
inner.y() + shift.y());
|
|
||||||
emojiToggle->update();
|
|
||||||
}, emojiToggle->lifetime());
|
|
||||||
|
|
||||||
emojiToggle->installEventFilter(emojiPanel);
|
|
||||||
emojiToggle->addClickHandler([=] {
|
|
||||||
updateEmojiPanelGeometry();
|
|
||||||
emojiPanel->toggleAnimated();
|
|
||||||
});
|
|
||||||
const auto filterCallback = [=](not_null<QEvent*> event) {
|
|
||||||
if (event->type() == QEvent::Enter) {
|
|
||||||
updateEmojiPanelGeometry();
|
|
||||||
}
|
|
||||||
return base::EventFilterResult::Continue;
|
|
||||||
};
|
|
||||||
base::install_event_filter(emojiToggle, filterCallback);
|
|
||||||
|
|
||||||
return emojiToggle;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Options {
|
class Options {
|
||||||
public:
|
public:
|
||||||
Options(
|
Options(
|
||||||
|
@ -770,7 +678,7 @@ void Options::addEmptyOption() {
|
||||||
_chooseCorrectGroup));
|
_chooseCorrectGroup));
|
||||||
const auto field = _list.back()->field();
|
const auto field = _list.back()->field();
|
||||||
if (const auto emojiPanel = _emojiPanel) {
|
if (const auto emojiPanel = _emojiPanel) {
|
||||||
const auto emojiToggle = AddEmojiToggleToField(
|
const auto emojiToggle = Ui::AddEmojiToggleToField(
|
||||||
field,
|
field,
|
||||||
_box,
|
_box,
|
||||||
_controller,
|
_controller,
|
||||||
|
@ -972,7 +880,7 @@ not_null<Ui::InputField*> CreatePollBox::setupQuestion(
|
||||||
emojiPanel->hide();
|
emojiPanel->hide();
|
||||||
emojiPanel->selector()->setCurrentPeer(session->user());
|
emojiPanel->selector()->setCurrentPeer(session->user());
|
||||||
|
|
||||||
const auto emojiToggle = AddEmojiToggleToField(
|
const auto emojiToggle = Ui::AddEmojiToggleToField(
|
||||||
question,
|
question,
|
||||||
this,
|
this,
|
||||||
_controller,
|
_controller,
|
||||||
|
|
|
@ -195,7 +195,7 @@ PaintRoundImageCallback PremiumsRow::generatePaintUserpicCallback(
|
||||||
const auto radius = size * Ui::ForumUserpicRadiusMultiplier();
|
const auto radius = size * Ui::ForumUserpicRadiusMultiplier();
|
||||||
p.drawRoundedRect(x, y, size, size, radius, radius);
|
p.drawRoundedRect(x, y, size, size, radius, radius);
|
||||||
}
|
}
|
||||||
st::settingsPrivacyPremium.paintInCenter(p, { x, y, size, size });
|
st::settingsPrivacyPremium.paintInCenter(p, QRect(x, y, size, size));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
#include "ui/wrap/slide_wrap.h"
|
#include "ui/wrap/slide_wrap.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
|
#include "ui/rect.h"
|
||||||
#include "ui/vertical_list.h"
|
#include "ui/vertical_list.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "styles/style_info.h"
|
#include "styles/style_info.h"
|
||||||
|
@ -581,6 +582,7 @@ void LinkController::addLinkBlock(not_null<Ui::VerticalLayout*> container) {
|
||||||
});
|
});
|
||||||
const auto getLinkQr = crl::guard(weak, [=] {
|
const auto getLinkQr = crl::guard(weak, [=] {
|
||||||
delegate()->peerListUiShow()->showBox(InviteLinkQrBox(
|
delegate()->peerListUiShow()->showBox(InviteLinkQrBox(
|
||||||
|
nullptr,
|
||||||
link,
|
link,
|
||||||
tr::lng_group_invite_qr_title(),
|
tr::lng_group_invite_qr_title(),
|
||||||
tr::lng_filters_link_qr_about()));
|
tr::lng_filters_link_qr_about()));
|
||||||
|
@ -889,6 +891,7 @@ base::unique_qptr<Ui::PopupMenu> LinksController::createRowContextMenu(
|
||||||
};
|
};
|
||||||
const auto getLinkQr = [=] {
|
const auto getLinkQr = [=] {
|
||||||
delegate()->peerListUiShow()->showBox(InviteLinkQrBox(
|
delegate()->peerListUiShow()->showBox(InviteLinkQrBox(
|
||||||
|
nullptr,
|
||||||
link,
|
link,
|
||||||
tr::lng_group_invite_qr_title(),
|
tr::lng_group_invite_qr_title(),
|
||||||
tr::lng_filters_link_qr_about()));
|
tr::lng_filters_link_qr_about()));
|
||||||
|
@ -965,9 +968,9 @@ void LinksController::rowPaintIcon(
|
||||||
p.setBrush(*bg);
|
p.setBrush(*bg);
|
||||||
{
|
{
|
||||||
auto hq = PainterHighQualityEnabler(p);
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
p.drawEllipse(QRect(0, 0, inner, inner));
|
p.drawEllipse(Rect(Size(inner)));
|
||||||
}
|
}
|
||||||
st::inviteLinkIcon.paintInCenter(p, { 0, 0, inner, inner });
|
st::inviteLinkIcon.paintInCenter(p, Rect(Size(inner)));
|
||||||
}
|
}
|
||||||
p.drawImage(x + skip, y + skip, icon);
|
p.drawImage(x + skip, y + skip, icon);
|
||||||
}
|
}
|
||||||
|
@ -1113,7 +1116,7 @@ QString FilterChatStatusText(not_null<PeerData*> peer) {
|
||||||
? tr::lng_chat_status_subscribers
|
? tr::lng_chat_status_subscribers
|
||||||
: tr::lng_chat_status_members)(
|
: tr::lng_chat_status_members)(
|
||||||
tr::now,
|
tr::now,
|
||||||
lt_count,
|
lt_count_decimal,
|
||||||
channel->membersCount());
|
channel->membersCount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,7 +98,7 @@ void GiftCreditsBox(
|
||||||
) | rpl::map([](TextWithEntities text) {
|
) | rpl::map([](TextWithEntities text) {
|
||||||
return Ui::Text::Link(
|
return Ui::Text::Link(
|
||||||
std::move(text),
|
std::move(text),
|
||||||
tr::lng_credits_box_history_entry_gift_about_url(tr::now));
|
u"internal:stars_examples"_q);
|
||||||
});
|
});
|
||||||
content->add(
|
content->add(
|
||||||
object_ptr<Ui::CenterWrap<>>(
|
object_ptr<Ui::CenterWrap<>>(
|
||||||
|
|
|
@ -69,6 +69,24 @@ constexpr auto kUserpicsMax = size_t(3);
|
||||||
using GiftOption = Data::PremiumSubscriptionOption;
|
using GiftOption = Data::PremiumSubscriptionOption;
|
||||||
using GiftOptions = Data::PremiumSubscriptionOptions;
|
using GiftOptions = Data::PremiumSubscriptionOptions;
|
||||||
|
|
||||||
|
[[nodiscard]] QString CreateMessageLink(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
PeerId peerId,
|
||||||
|
uint64 messageId) {
|
||||||
|
if (const auto msgId = MsgId(peerId ? messageId : 0)) {
|
||||||
|
const auto peer = session->data().peer(peerId);
|
||||||
|
if (const auto channel = peer->asBroadcast()) {
|
||||||
|
const auto username = channel->username();
|
||||||
|
const auto base = username.isEmpty()
|
||||||
|
? u"c/%1"_q.arg(peerToChannel(channel->id).bare)
|
||||||
|
: username;
|
||||||
|
const auto query = base + '/' + QString::number(msgId.bare);
|
||||||
|
return session->createInternalLink(query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
};
|
||||||
|
|
||||||
GiftOptions GiftOptionFromTL(const MTPDuserFull &data) {
|
GiftOptions GiftOptionFromTL(const MTPDuserFull &data) {
|
||||||
auto result = GiftOptions();
|
auto result = GiftOptions();
|
||||||
const auto gifts = data.vpremium_gifts();
|
const auto gifts = data.vpremium_gifts();
|
||||||
|
@ -1419,21 +1437,56 @@ void GiveawayInfoBox(
|
||||||
: !start->channels.empty()
|
: !start->channels.empty()
|
||||||
? start->channels.front()->name()
|
? start->channels.front()->name()
|
||||||
: u"channel"_q;
|
: u"channel"_q;
|
||||||
auto text = TextWithEntities();
|
|
||||||
|
|
||||||
if (!info.giftCode.isEmpty()) {
|
auto resultText = (!info.giftCode.isEmpty())
|
||||||
text.append("\n\n");
|
? tr::lng_prizes_you_won(
|
||||||
text.append(Ui::Text::Bold(tr::lng_prizes_you_won(
|
|
||||||
tr::now,
|
|
||||||
lt_cup,
|
lt_cup,
|
||||||
QString::fromUtf8("\xf0\x9f\x8f\x86"))));
|
rpl::single(
|
||||||
text.append("\n\n");
|
TextWithEntities{ QString::fromUtf8("\xf0\x9f\x8f\x86") }),
|
||||||
} else if (info.state == State::Finished) {
|
Ui::Text::WithEntities)
|
||||||
text.append("\n\n");
|
: (info.credits)
|
||||||
text.append(Ui::Text::Bold(tr::lng_prizes_you_didnt(tr::now)));
|
? tr::lng_prizes_you_won_credits(
|
||||||
text.append("\n\n");
|
lt_amount,
|
||||||
|
tr::lng_prizes_you_won_credits_amount(
|
||||||
|
lt_count,
|
||||||
|
rpl::single(float64(info.credits)),
|
||||||
|
Ui::Text::Bold),
|
||||||
|
lt_cup,
|
||||||
|
rpl::single(
|
||||||
|
TextWithEntities{ QString::fromUtf8("\xf0\x9f\x8f\x86") }),
|
||||||
|
Ui::Text::WithEntities)
|
||||||
|
: (info.state == State::Finished)
|
||||||
|
? tr::lng_prizes_you_didnt(Ui::Text::WithEntities)
|
||||||
|
: (rpl::producer<TextWithEntities>)(nullptr);
|
||||||
|
|
||||||
|
if (resultText) {
|
||||||
|
const auto &st = st::changePhoneDescription;
|
||||||
|
const auto skip = st.style.font->height * 0.5;
|
||||||
|
auto label = object_ptr<Ui::FlatLabel>(
|
||||||
|
box.get(),
|
||||||
|
std::move(resultText),
|
||||||
|
st);
|
||||||
|
if ((!info.giftCode.isEmpty()) || info.credits) {
|
||||||
|
label->setTextColorOverride(st::windowActiveTextFg->c);
|
||||||
|
}
|
||||||
|
const auto result = box->addRow(
|
||||||
|
object_ptr<Ui::PaddingWrap<Ui::CenterWrap<Ui::FlatLabel>>>(
|
||||||
|
box.get(),
|
||||||
|
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||||
|
box.get(),
|
||||||
|
std::move(label)),
|
||||||
|
QMargins(0, skip, 0, skip)));
|
||||||
|
result->paintRequest() | rpl::start_with_next([=] {
|
||||||
|
auto p = QPainter(result);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
p.setBrush(st::boxDividerBg);
|
||||||
|
p.drawRoundedRect(result->rect(), st::boxRadius, st::boxRadius);
|
||||||
|
}, result->lifetime());
|
||||||
|
Ui::AddSkip(box->verticalLayout());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto text = TextWithEntities();
|
||||||
|
|
||||||
const auto quantity = start
|
const auto quantity = start
|
||||||
? start->quantity
|
? start->quantity
|
||||||
: (results->winnersCount + results->unclaimedCount);
|
: (results->winnersCount + results->unclaimedCount);
|
||||||
|
@ -1442,22 +1495,39 @@ void GiveawayInfoBox(
|
||||||
? results->channel->isMegagroup()
|
? results->channel->isMegagroup()
|
||||||
: (!start->channels.empty()
|
: (!start->channels.empty()
|
||||||
&& start->channels.front()->isMegagroup());
|
&& start->channels.front()->isMegagroup());
|
||||||
|
const auto credits = start
|
||||||
|
? start->credits
|
||||||
|
: (results ? results->credits : 0);
|
||||||
text.append((finished
|
text.append((finished
|
||||||
? tr::lng_prizes_end_text
|
? tr::lng_prizes_end_text
|
||||||
: tr::lng_prizes_how_text)(
|
: tr::lng_prizes_how_text)(
|
||||||
tr::now,
|
tr::now,
|
||||||
lt_admins,
|
lt_admins,
|
||||||
(group
|
credits
|
||||||
? tr::lng_prizes_admins_group
|
? (group
|
||||||
: tr::lng_prizes_admins)(
|
? tr::lng_prizes_credits_admins_group
|
||||||
tr::now,
|
: tr::lng_prizes_credits_admins)(
|
||||||
lt_count,
|
tr::now,
|
||||||
quantity,
|
lt_channel,
|
||||||
lt_channel,
|
Ui::Text::Bold(first),
|
||||||
Ui::Text::Bold(first),
|
lt_amount,
|
||||||
lt_duration,
|
tr::lng_prizes_credits_admins_amount(
|
||||||
TextWithEntities{ GiftDuration(months) },
|
tr::now,
|
||||||
Ui::Text::RichLangValue),
|
lt_count_decimal,
|
||||||
|
float64(credits),
|
||||||
|
Ui::Text::Bold),
|
||||||
|
Ui::Text::RichLangValue)
|
||||||
|
: (group
|
||||||
|
? tr::lng_prizes_admins_group
|
||||||
|
: tr::lng_prizes_admins)(
|
||||||
|
tr::now,
|
||||||
|
lt_count,
|
||||||
|
quantity,
|
||||||
|
lt_channel,
|
||||||
|
Ui::Text::Bold(first),
|
||||||
|
lt_duration,
|
||||||
|
TextWithEntities{ GiftDuration(months) },
|
||||||
|
Ui::Text::RichLangValue),
|
||||||
Ui::Text::RichLangValue));
|
Ui::Text::RichLangValue));
|
||||||
const auto many = start
|
const auto many = start
|
||||||
? (start->channels.size() > 1)
|
? (start->channels.size() > 1)
|
||||||
|
@ -1651,6 +1721,7 @@ void AddCreditsHistoryEntryTable(
|
||||||
st::giveawayGiftCodeTable),
|
st::giveawayGiftCodeTable),
|
||||||
st::giveawayGiftCodeTableMargin);
|
st::giveawayGiftCodeTableMargin);
|
||||||
const auto peerId = PeerId(entry.barePeerId);
|
const auto peerId = PeerId(entry.barePeerId);
|
||||||
|
const auto session = &controller->session();
|
||||||
if (peerId) {
|
if (peerId) {
|
||||||
auto text = entry.in
|
auto text = entry.in
|
||||||
? tr::lng_credits_box_history_entry_peer_in()
|
? tr::lng_credits_box_history_entry_peer_in()
|
||||||
|
@ -1658,15 +1729,12 @@ void AddCreditsHistoryEntryTable(
|
||||||
AddTableRow(table, std::move(text), controller, peerId);
|
AddTableRow(table, std::move(text), controller, peerId);
|
||||||
}
|
}
|
||||||
if (const auto msgId = MsgId(peerId ? entry.bareMsgId : 0)) {
|
if (const auto msgId = MsgId(peerId ? entry.bareMsgId : 0)) {
|
||||||
const auto session = &controller->session();
|
|
||||||
const auto peer = session->data().peer(peerId);
|
const auto peer = session->data().peer(peerId);
|
||||||
if (const auto channel = peer->asBroadcast()) {
|
if (const auto channel = peer->asBroadcast()) {
|
||||||
const auto username = channel->username();
|
const auto link = CreateMessageLink(
|
||||||
const auto base = username.isEmpty()
|
session,
|
||||||
? u"c/%1"_q.arg(peerToChannel(channel->id).bare)
|
peerId,
|
||||||
: username;
|
entry.bareMsgId);
|
||||||
const auto query = base + '/' + QString::number(msgId.bare);
|
|
||||||
const auto link = session->createInternalLink(query);
|
|
||||||
auto label = object_ptr<Ui::FlatLabel>(
|
auto label = object_ptr<Ui::FlatLabel>(
|
||||||
table,
|
table,
|
||||||
rpl::single(Ui::Text::Link(link)),
|
rpl::single(Ui::Text::Link(link)),
|
||||||
|
@ -1717,6 +1785,37 @@ void AddCreditsHistoryEntryTable(
|
||||||
tr::lng_credits_box_history_entry_via_premium_bot(
|
tr::lng_credits_box_history_entry_via_premium_bot(
|
||||||
Ui::Text::RichLangValue));
|
Ui::Text::RichLangValue));
|
||||||
}
|
}
|
||||||
|
if (entry.bareGiveawayMsgId) {
|
||||||
|
AddTableRow(
|
||||||
|
table,
|
||||||
|
tr::lng_gift_link_label_to(),
|
||||||
|
controller,
|
||||||
|
controller->session().userId());
|
||||||
|
}
|
||||||
|
if (entry.bareGiveawayMsgId && entry.credits) {
|
||||||
|
AddTableRow(
|
||||||
|
table,
|
||||||
|
tr::lng_gift_link_label_gift(),
|
||||||
|
tr::lng_gift_stars_title(
|
||||||
|
lt_count,
|
||||||
|
rpl::single(float64(entry.credits)),
|
||||||
|
Ui::Text::RichLangValue));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const auto link = CreateMessageLink(
|
||||||
|
session,
|
||||||
|
peerId,
|
||||||
|
entry.bareGiveawayMsgId);
|
||||||
|
if (!link.isEmpty()) {
|
||||||
|
AddTableRow(
|
||||||
|
table,
|
||||||
|
tr::lng_gift_link_label_reason(),
|
||||||
|
tr::lng_gift_link_reason_giveaway(
|
||||||
|
) | rpl::map([link](const QString &text) {
|
||||||
|
return Ui::Text::Link(text, link);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!entry.id.isEmpty()) {
|
if (!entry.id.isEmpty()) {
|
||||||
constexpr auto kOneLineCount = 18;
|
constexpr auto kOneLineCount = 18;
|
||||||
const auto oneLine = entry.id.length() <= kOneLineCount;
|
const auto oneLine = entry.id.length() <= kOneLineCount;
|
||||||
|
@ -1813,3 +1912,60 @@ void AddSubscriberEntryTable(
|
||||||
rpl::single(Ui::Text::WithEntities(langDateTime(d))));
|
rpl::single(Ui::Text::WithEntities(langDateTime(d))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AddCreditsBoostTable(
|
||||||
|
not_null<Window::SessionNavigation*> controller,
|
||||||
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
const Data::Boost &b) {
|
||||||
|
auto table = container->add(
|
||||||
|
object_ptr<Ui::TableLayout>(
|
||||||
|
container,
|
||||||
|
st::giveawayGiftCodeTable),
|
||||||
|
st::giveawayGiftCodeTableMargin);
|
||||||
|
const auto peerId = b.giveawayMessage.peer;
|
||||||
|
if (!peerId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto from = controller->session().data().peer(peerId);
|
||||||
|
AddTableRow(
|
||||||
|
table,
|
||||||
|
tr::lng_credits_box_history_entry_peer_in(),
|
||||||
|
controller,
|
||||||
|
from->id);
|
||||||
|
if (b.credits) {
|
||||||
|
AddTableRow(
|
||||||
|
table,
|
||||||
|
tr::lng_gift_link_label_gift(),
|
||||||
|
tr::lng_gift_stars_title(
|
||||||
|
lt_count,
|
||||||
|
rpl::single(float64(b.credits)),
|
||||||
|
Ui::Text::RichLangValue));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const auto link = CreateMessageLink(
|
||||||
|
&controller->session(),
|
||||||
|
peerId,
|
||||||
|
b.giveawayMessage.msg.bare);
|
||||||
|
if (!link.isEmpty()) {
|
||||||
|
AddTableRow(
|
||||||
|
table,
|
||||||
|
tr::lng_gift_link_label_reason(),
|
||||||
|
tr::lng_gift_link_reason_giveaway(
|
||||||
|
) | rpl::map([link](const QString &text) {
|
||||||
|
return Ui::Text::Link(text, link);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!b.date.isNull()) {
|
||||||
|
AddTableRow(
|
||||||
|
table,
|
||||||
|
tr::lng_gift_link_label_date(),
|
||||||
|
rpl::single(Ui::Text::WithEntities(langDateTime(b.date))));
|
||||||
|
}
|
||||||
|
if (!b.expiresAt.isNull()) {
|
||||||
|
AddTableRow(
|
||||||
|
table,
|
||||||
|
tr::lng_gift_until(),
|
||||||
|
rpl::single(Ui::Text::WithEntities(langDateTime(b.expiresAt))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ struct GiftCode;
|
||||||
} // namespace Api
|
} // namespace Api
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
struct Boost;
|
||||||
struct CreditsHistoryEntry;
|
struct CreditsHistoryEntry;
|
||||||
struct GiveawayStart;
|
struct GiveawayStart;
|
||||||
struct GiveawayResults;
|
struct GiveawayResults;
|
||||||
|
@ -89,3 +90,8 @@ void AddSubscriberEntryTable(
|
||||||
not_null<Ui::VerticalLayout*> container,
|
not_null<Ui::VerticalLayout*> container,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
TimeId date);
|
TimeId date);
|
||||||
|
|
||||||
|
void AddCreditsBoostTable(
|
||||||
|
not_null<Window::SessionNavigation*> controller,
|
||||||
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
const Data::Boost &boost);
|
||||||
|
|
|
@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/text/text_options.h"
|
#include "ui/text/text_options.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/vertical_list.h"
|
#include "ui/vertical_list.h"
|
||||||
|
#include "ui/ui_utility.h"
|
||||||
#include "storage/localstorage.h"
|
#include "storage/localstorage.h"
|
||||||
#include "boxes/abstract_box.h"
|
#include "boxes/abstract_box.h"
|
||||||
#include "boxes/premium_preview_box.h"
|
#include "boxes/premium_preview_box.h"
|
||||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "boxes/moderate_messages_box.h"
|
#include "boxes/moderate_messages_box.h"
|
||||||
|
|
||||||
|
#include "api/api_blocked_peers.h"
|
||||||
#include "api/api_chat_participants.h"
|
#include "api/api_chat_participants.h"
|
||||||
#include "api/api_messages_search.h"
|
#include "api/api_messages_search.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
|
@ -31,7 +32,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/boxes/confirm_box.h"
|
#include "ui/boxes/confirm_box.h"
|
||||||
#include "ui/controls/userpic_button.h"
|
#include "ui/controls/userpic_button.h"
|
||||||
#include "ui/effects/ripple_animation.h"
|
#include "ui/effects/ripple_animation.h"
|
||||||
#include "ui/effects/toggle_arrow.h"
|
|
||||||
#include "ui/layers/generic_box.h"
|
#include "ui/layers/generic_box.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/rect.h"
|
#include "ui/rect.h"
|
||||||
|
@ -39,15 +39,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/vertical_list.h"
|
#include "ui/vertical_list.h"
|
||||||
#include "ui/widgets/checkbox.h"
|
#include "ui/widgets/checkbox.h"
|
||||||
|
#include "ui/widgets/expandable_peer_list.h"
|
||||||
|
#include "ui/widgets/participants_check_view.h"
|
||||||
#include "ui/wrap/slide_wrap.h"
|
#include "ui/wrap/slide_wrap.h"
|
||||||
|
#include "ui/ui_utility.h"
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
#include "styles/style_layers.h"
|
#include "styles/style_layers.h"
|
||||||
#include "styles/style_window.h"
|
#include "styles/style_window.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using Participants = std::vector<not_null<PeerData*>>;
|
|
||||||
|
|
||||||
struct ModerateOptions final {
|
struct ModerateOptions final {
|
||||||
bool allCanBan = false;
|
bool allCanBan = false;
|
||||||
bool allCanDelete = false;
|
bool allCanDelete = false;
|
||||||
|
@ -103,117 +104,14 @@ ModerateOptions CalculateModerateOptions(const HistoryItemsList &items) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class Button final : public Ui::RippleButton {
|
|
||||||
public:
|
|
||||||
Button(not_null<QWidget*> parent, int count);
|
|
||||||
|
|
||||||
void setChecked(bool checked);
|
|
||||||
[[nodiscard]] bool checked() const;
|
|
||||||
|
|
||||||
[[nodiscard]] static QSize ComputeSize(int);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void paintEvent(QPaintEvent *event) override;
|
|
||||||
QImage prepareRippleMask() const override;
|
|
||||||
QPoint prepareRippleStartPosition() const override;
|
|
||||||
|
|
||||||
const int _count;
|
|
||||||
const QString _text;
|
|
||||||
bool _checked = false;
|
|
||||||
|
|
||||||
Ui::Animations::Simple _animation;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
Button::Button(not_null<QWidget*> parent, int count)
|
|
||||||
: RippleButton(parent, st::defaultRippleAnimation)
|
|
||||||
, _count(count)
|
|
||||||
, _text(QString::number(std::abs(_count))) {
|
|
||||||
}
|
|
||||||
|
|
||||||
QSize Button::ComputeSize(int count) {
|
|
||||||
return QSize(
|
|
||||||
st::moderateBoxExpandHeight
|
|
||||||
+ st::moderateBoxExpand.width()
|
|
||||||
+ st::moderateBoxExpandInnerSkip * 4
|
|
||||||
+ st::moderateBoxExpandFont->width(
|
|
||||||
QString::number(std::abs(count)))
|
|
||||||
+ st::moderateBoxExpandToggleSize,
|
|
||||||
st::moderateBoxExpandHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Button::setChecked(bool checked) {
|
|
||||||
if (_checked == checked) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_checked = checked;
|
|
||||||
_animation.stop();
|
|
||||||
_animation.start(
|
|
||||||
[=] { update(); },
|
|
||||||
checked ? 0 : 1,
|
|
||||||
checked ? 1 : 0,
|
|
||||||
st::slideWrapDuration);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Button::checked() const {
|
|
||||||
return _checked;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Button::paintEvent(QPaintEvent *event) {
|
|
||||||
auto p = Painter(this);
|
|
||||||
auto hq = PainterHighQualityEnabler(p);
|
|
||||||
Ui::RippleButton::paintRipple(p, QPoint());
|
|
||||||
const auto radius = height() / 2;
|
|
||||||
p.setPen(Qt::NoPen);
|
|
||||||
st::moderateBoxExpand.paint(
|
|
||||||
p,
|
|
||||||
radius,
|
|
||||||
(height() - st::moderateBoxExpand.height()) / 2,
|
|
||||||
width());
|
|
||||||
|
|
||||||
const auto innerSkip = st::moderateBoxExpandInnerSkip;
|
|
||||||
|
|
||||||
p.setBrush(Qt::NoBrush);
|
|
||||||
p.setPen(st::boxTextFg);
|
|
||||||
p.setFont(st::moderateBoxExpandFont);
|
|
||||||
p.drawText(
|
|
||||||
QRect(
|
|
||||||
innerSkip + radius + st::moderateBoxExpand.width(),
|
|
||||||
0,
|
|
||||||
width(),
|
|
||||||
height()),
|
|
||||||
_text,
|
|
||||||
style::al_left);
|
|
||||||
|
|
||||||
const auto path = Ui::ToggleUpDownArrowPath(
|
|
||||||
width() - st::moderateBoxExpandToggleSize - radius,
|
|
||||||
height() / 2,
|
|
||||||
st::moderateBoxExpandToggleSize,
|
|
||||||
st::moderateBoxExpandToggleFourStrokes,
|
|
||||||
_animation.value(_checked ? 1. : 0.));
|
|
||||||
p.fillPath(path, st::boxTextFg);
|
|
||||||
}
|
|
||||||
|
|
||||||
QImage Button::prepareRippleMask() const {
|
|
||||||
return Ui::RippleAnimation::RoundRectMask(size(), size().height() / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
QPoint Button::prepareRippleStartPosition() const {
|
|
||||||
return mapFromGlobal(QCursor::pos());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void CreateModerateMessagesBox(
|
void CreateModerateMessagesBox(
|
||||||
not_null<Ui::GenericBox*> box,
|
not_null<Ui::GenericBox*> box,
|
||||||
const HistoryItemsList &items,
|
const HistoryItemsList &items,
|
||||||
Fn<void()> confirmed) {
|
Fn<void()> confirmed) {
|
||||||
struct Controller final {
|
using Controller = Ui::ExpandablePeerListController;
|
||||||
rpl::event_stream<bool> toggleRequestsFromTop;
|
|
||||||
rpl::event_stream<bool> toggleRequestsFromInner;
|
|
||||||
rpl::event_stream<bool> checkAllRequests;
|
|
||||||
Fn<Participants()> collectRequests;
|
|
||||||
};
|
|
||||||
const auto [allCanBan, allCanDelete, participants]
|
const auto [allCanBan, allCanDelete, participants]
|
||||||
= CalculateModerateOptions(items);
|
= CalculateModerateOptions(items);
|
||||||
const auto inner = box->verticalLayout();
|
const auto inner = box->verticalLayout();
|
||||||
|
@ -225,7 +123,12 @@ void CreateModerateMessagesBox(
|
||||||
const auto isSingle = participants.size() == 1;
|
const auto isSingle = participants.size() == 1;
|
||||||
const auto buttonPadding = isSingle
|
const auto buttonPadding = isSingle
|
||||||
? QMargins()
|
? QMargins()
|
||||||
: QMargins(0, 0, Button::ComputeSize(participants.size()).width(), 0);
|
: QMargins(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
Ui::ParticipantsCheckView::ComputeSize(
|
||||||
|
participants.size()).width(),
|
||||||
|
0);
|
||||||
|
|
||||||
const auto session = &items.front()->history()->session();
|
const auto session = &items.front()->history()->session();
|
||||||
const auto historyPeerId = items.front()->history()->peer->id;
|
const auto historyPeerId = items.front()->history()->peer->id;
|
||||||
|
@ -307,135 +210,6 @@ void CreateModerateMessagesBox(
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto createParticipantsList = [&](
|
|
||||||
not_null<Controller*> controller) {
|
|
||||||
const auto wrap = inner->add(
|
|
||||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
|
||||||
inner,
|
|
||||||
object_ptr<Ui::VerticalLayout>(inner)));
|
|
||||||
wrap->toggle(false, anim::type::instant);
|
|
||||||
|
|
||||||
controller->toggleRequestsFromTop.events(
|
|
||||||
) | rpl::start_with_next([=](bool toggled) {
|
|
||||||
wrap->toggle(toggled, anim::type::normal);
|
|
||||||
}, wrap->lifetime());
|
|
||||||
|
|
||||||
const auto container = wrap->entity();
|
|
||||||
Ui::AddSkip(container);
|
|
||||||
|
|
||||||
auto &lifetime = wrap->lifetime();
|
|
||||||
const auto clicks = lifetime.make_state<rpl::event_stream<>>();
|
|
||||||
const auto checkboxes = ranges::views::all(
|
|
||||||
participants
|
|
||||||
) | ranges::views::transform([&](not_null<PeerData*> peer) {
|
|
||||||
const auto line = container->add(
|
|
||||||
object_ptr<Ui::AbstractButton>(container));
|
|
||||||
const auto &st = st::moderateBoxUserpic;
|
|
||||||
line->resize(line->width(), st.size.height());
|
|
||||||
|
|
||||||
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
|
|
||||||
line,
|
|
||||||
peer,
|
|
||||||
st);
|
|
||||||
const auto checkbox = Ui::CreateChild<Ui::Checkbox>(
|
|
||||||
line,
|
|
||||||
peer->name(),
|
|
||||||
false,
|
|
||||||
st::defaultBoxCheckbox);
|
|
||||||
line->widthValue(
|
|
||||||
) | rpl::start_with_next([=](int width) {
|
|
||||||
userpic->moveToLeft(
|
|
||||||
st::boxRowPadding.left()
|
|
||||||
+ checkbox->checkRect().width()
|
|
||||||
+ st::defaultBoxCheckbox.textPosition.x(),
|
|
||||||
0);
|
|
||||||
const auto skip = st::defaultBoxCheckbox.textPosition.x();
|
|
||||||
checkbox->resizeToWidth(width
|
|
||||||
- rect::right(userpic)
|
|
||||||
- skip
|
|
||||||
- st::boxRowPadding.right());
|
|
||||||
checkbox->moveToLeft(
|
|
||||||
rect::right(userpic) + skip,
|
|
||||||
((userpic->height() - checkbox->height()) / 2)
|
|
||||||
+ st::defaultBoxCheckbox.margin.top());
|
|
||||||
}, checkbox->lifetime());
|
|
||||||
|
|
||||||
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
||||||
checkbox->setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
||||||
|
|
||||||
line->setClickedCallback([=] {
|
|
||||||
checkbox->setChecked(!checkbox->checked());
|
|
||||||
clicks->fire({});
|
|
||||||
});
|
|
||||||
|
|
||||||
return checkbox;
|
|
||||||
}) | ranges::to_vector;
|
|
||||||
|
|
||||||
clicks->events(
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
controller->toggleRequestsFromInner.fire_copy(
|
|
||||||
ranges::any_of(checkboxes, &Ui::Checkbox::checked));
|
|
||||||
}, container->lifetime());
|
|
||||||
|
|
||||||
controller->checkAllRequests.events(
|
|
||||||
) | rpl::start_with_next([=](bool checked) {
|
|
||||||
for (const auto &c : checkboxes) {
|
|
||||||
c->setChecked(checked);
|
|
||||||
}
|
|
||||||
}, container->lifetime());
|
|
||||||
|
|
||||||
controller->collectRequests = [=] {
|
|
||||||
auto result = Participants();
|
|
||||||
for (auto i = 0; i < checkboxes.size(); i++) {
|
|
||||||
if (checkboxes[i]->checked()) {
|
|
||||||
result.push_back(participants[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto appendList = [&](
|
|
||||||
not_null<Ui::Checkbox*> checkbox,
|
|
||||||
not_null<Controller*> controller) {
|
|
||||||
if (isSingle) {
|
|
||||||
const auto p = participants.front();
|
|
||||||
controller->collectRequests = [=] { return Participants{ p }; };
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto count = int(participants.size());
|
|
||||||
const auto button = Ui::CreateChild<Button>(inner, count);
|
|
||||||
button->resize(Button::ComputeSize(count));
|
|
||||||
|
|
||||||
const auto overlay = Ui::CreateChild<Ui::AbstractButton>(inner);
|
|
||||||
|
|
||||||
checkbox->geometryValue(
|
|
||||||
) | rpl::start_with_next([=](const QRect &rect) {
|
|
||||||
overlay->setGeometry(rect);
|
|
||||||
overlay->raise();
|
|
||||||
|
|
||||||
button->moveToRight(
|
|
||||||
st::moderateBoxExpandRight,
|
|
||||||
rect.top() + (rect.height() - button->height()) / 2,
|
|
||||||
box->width());
|
|
||||||
button->raise();
|
|
||||||
}, button->lifetime());
|
|
||||||
|
|
||||||
controller->toggleRequestsFromInner.events(
|
|
||||||
) | rpl::start_with_next([=](bool toggled) {
|
|
||||||
checkbox->setChecked(toggled);
|
|
||||||
}, checkbox->lifetime());
|
|
||||||
button->setClickedCallback([=] {
|
|
||||||
button->setChecked(!button->checked());
|
|
||||||
controller->toggleRequestsFromTop.fire_copy(button->checked());
|
|
||||||
});
|
|
||||||
overlay->setClickedCallback([=] {
|
|
||||||
checkbox->setChecked(!checkbox->checked());
|
|
||||||
controller->checkAllRequests.fire_copy(checkbox->checked());
|
|
||||||
});
|
|
||||||
createParticipantsList(controller);
|
|
||||||
};
|
|
||||||
|
|
||||||
Ui::AddSkip(inner);
|
Ui::AddSkip(inner);
|
||||||
const auto title = box->addRow(
|
const auto title = box->addRow(
|
||||||
object_ptr<Ui::FlatLabel>(
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
@ -457,8 +231,9 @@ void CreateModerateMessagesBox(
|
||||||
false,
|
false,
|
||||||
st::defaultBoxCheckbox),
|
st::defaultBoxCheckbox),
|
||||||
st::boxRowPadding + buttonPadding);
|
st::boxRowPadding + buttonPadding);
|
||||||
const auto controller = box->lifetime().make_state<Controller>();
|
const auto controller = box->lifetime().make_state<Controller>(
|
||||||
appendList(report, controller);
|
Controller::Data{ .participants = participants });
|
||||||
|
Ui::AddExpandablePeerList(report, controller, inner);
|
||||||
handleSubmition(report);
|
handleSubmition(report);
|
||||||
|
|
||||||
const auto ids = items.front()->from()->owner().itemsToIds(items);
|
const auto ids = items.front()->from()->owner().itemsToIds(items);
|
||||||
|
@ -515,8 +290,9 @@ void CreateModerateMessagesBox(
|
||||||
}, title->lifetime());
|
}, title->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto controller = box->lifetime().make_state<Controller>();
|
const auto controller = box->lifetime().make_state<Controller>(
|
||||||
appendList(deleteAll, controller);
|
Controller::Data{ .participants = participants });
|
||||||
|
Ui::AddExpandablePeerList(deleteAll, controller, inner);
|
||||||
handleSubmition(deleteAll);
|
handleSubmition(deleteAll);
|
||||||
|
|
||||||
handleConfirmation(deleteAll, controller, [=](
|
handleConfirmation(deleteAll, controller, [=](
|
||||||
|
@ -545,8 +321,9 @@ void CreateModerateMessagesBox(
|
||||||
false,
|
false,
|
||||||
st::defaultBoxCheckbox),
|
st::defaultBoxCheckbox),
|
||||||
st::boxRowPadding + buttonPadding);
|
st::boxRowPadding + buttonPadding);
|
||||||
const auto controller = box->lifetime().make_state<Controller>();
|
const auto controller = box->lifetime().make_state<Controller>(
|
||||||
appendList(ban, controller);
|
Controller::Data{ .participants = participants });
|
||||||
|
Ui::AddExpandablePeerList(ban, controller, inner);
|
||||||
handleSubmition(ban);
|
handleSubmition(ban);
|
||||||
|
|
||||||
Ui::AddSkip(inner);
|
Ui::AddSkip(inner);
|
||||||
|
@ -659,14 +436,16 @@ void CreateModerateMessagesBox(
|
||||||
return result;
|
return result;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
auto [checkboxes, getRestrictions, changes] = CreateEditRestrictions(
|
Ui::AddSubsectionTitle(
|
||||||
box,
|
inner,
|
||||||
rpl::conditional(
|
rpl::conditional(
|
||||||
rpl::single(isSingle),
|
rpl::single(isSingle),
|
||||||
tr::lng_restrict_users_part_single_header(),
|
tr::lng_restrict_users_part_single_header(),
|
||||||
tr::lng_restrict_users_part_header(
|
tr::lng_restrict_users_part_header(
|
||||||
lt_count,
|
lt_count,
|
||||||
rpl::single(participants.size()) | tr::to_count())),
|
rpl::single(participants.size()) | tr::to_count())));
|
||||||
|
auto [checkboxes, getRestrictions, changes] = CreateEditRestrictions(
|
||||||
|
box,
|
||||||
prepareFlags,
|
prepareFlags,
|
||||||
disabledMessages,
|
disabledMessages,
|
||||||
{ .isForum = peer->isForum() });
|
{ .isForum = peer->isForum() });
|
||||||
|
@ -757,38 +536,24 @@ void DeleteChatBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) {
|
||||||
return base::EventFilterResult::Continue;
|
return base::EventFilterResult::Continue;
|
||||||
});
|
});
|
||||||
|
|
||||||
const auto line = container->add(object_ptr<Ui::RpWidget>(container));
|
|
||||||
const auto &st = st::mainMenuUserpic;
|
|
||||||
line->resize(line->width(), st.size.height());
|
|
||||||
|
|
||||||
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
|
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
|
||||||
line,
|
container,
|
||||||
peer,
|
peer,
|
||||||
st);
|
st::mainMenuUserpic);
|
||||||
userpic->showSavedMessagesOnSelf(true);
|
userpic->showSavedMessagesOnSelf(true);
|
||||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
Ui::IconWithTitle(
|
||||||
line,
|
container,
|
||||||
peer->isSelf()
|
userpic,
|
||||||
? tr::lng_saved_messages() | Ui::Text::ToBold()
|
Ui::CreateChild<Ui::FlatLabel>(
|
||||||
: maybeUser
|
container,
|
||||||
? tr::lng_profile_delete_conversation() | Ui::Text::ToBold()
|
peer->isSelf()
|
||||||
: rpl::single(Ui::Text::Bold(peer->name())) | rpl::type_erased(),
|
? tr::lng_saved_messages() | Ui::Text::ToBold()
|
||||||
box->getDelegate()->style().title);
|
: maybeUser
|
||||||
line->widthValue(
|
? tr::lng_profile_delete_conversation() | Ui::Text::ToBold()
|
||||||
) | rpl::start_with_next([=](int width) {
|
: rpl::single(
|
||||||
userpic->moveToLeft(st::boxRowPadding.left(), 0);
|
peer->name()
|
||||||
const auto skip = st::defaultBoxCheckbox.textPosition.x();
|
) | Ui::Text::ToBold() | rpl::type_erased(),
|
||||||
label->resizeToWidth(width
|
box->getDelegate()->style().title));
|
||||||
- rect::right(userpic)
|
|
||||||
- skip
|
|
||||||
- st::boxRowPadding.right());
|
|
||||||
label->moveToLeft(
|
|
||||||
rect::right(userpic) + skip,
|
|
||||||
((userpic->height() - label->height()) / 2));
|
|
||||||
}, label->lifetime());
|
|
||||||
|
|
||||||
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
||||||
label->setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
||||||
|
|
||||||
Ui::AddSkip(container);
|
Ui::AddSkip(container);
|
||||||
Ui::AddSkip(container);
|
Ui::AddSkip(container);
|
||||||
|
@ -829,6 +594,20 @@ void DeleteChatBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) {
|
||||||
st::defaultBoxCheckbox));
|
st::defaultBoxCheckbox));
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
const auto maybeBotCheckbox = [&]() -> Ui::Checkbox* {
|
||||||
|
if (!maybeUser || !maybeUser->isBot()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
Ui::AddSkip(container);
|
||||||
|
Ui::AddSkip(container);
|
||||||
|
return box->addRow(
|
||||||
|
object_ptr<Ui::Checkbox>(
|
||||||
|
container,
|
||||||
|
tr::lng_profile_block_bot(tr::now, Ui::Text::WithEntities),
|
||||||
|
false,
|
||||||
|
st::defaultBoxCheckbox));
|
||||||
|
}();
|
||||||
|
|
||||||
Ui::AddSkip(container);
|
Ui::AddSkip(container);
|
||||||
|
|
||||||
auto buttonText = maybeUser
|
auto buttonText = maybeUser
|
||||||
|
@ -842,7 +621,11 @@ void DeleteChatBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) {
|
||||||
const auto close = crl::guard(box, [=] { box->closeBox(); });
|
const auto close = crl::guard(box, [=] { box->closeBox(); });
|
||||||
box->addButton(std::move(buttonText), [=] {
|
box->addButton(std::move(buttonText), [=] {
|
||||||
const auto revoke = maybeCheckbox && maybeCheckbox->checked();
|
const auto revoke = maybeCheckbox && maybeCheckbox->checked();
|
||||||
|
const auto stopBot = maybeBotCheckbox && maybeBotCheckbox->checked();
|
||||||
Core::App().closeChatFromWindows(peer);
|
Core::App().closeChatFromWindows(peer);
|
||||||
|
if (stopBot) {
|
||||||
|
peer->session().api().blockedPeers().block(peer);
|
||||||
|
}
|
||||||
// Don't delete old history by default,
|
// Don't delete old history by default,
|
||||||
// because Android app doesn't.
|
// because Android app doesn't.
|
||||||
//
|
//
|
||||||
|
|
|
@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/wrap/slide_wrap.h"
|
#include "ui/wrap/slide_wrap.h"
|
||||||
#include "ui/text/text_options.h"
|
#include "ui/text/text_options.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
|
#include "ui/ui_utility.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "storage/file_download.h"
|
#include "storage/file_download.h"
|
||||||
#include "data/data_peer_values.h"
|
#include "data/data_peer_values.h"
|
||||||
|
|
|
@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
#include "ui/widgets/multi_select.h"
|
#include "ui/widgets/multi_select.h"
|
||||||
#include "ui/widgets/scroll_area.h"
|
#include "ui/widgets/scroll_area.h"
|
||||||
|
#include "ui/ui_utility.h"
|
||||||
#include "main/session/session_show.h"
|
#include "main/session/session_show.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
|
|
@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/boxes/confirm_box.h"
|
#include "ui/boxes/confirm_box.h"
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
#include "ui/wrap/slide_wrap.h"
|
#include "ui/wrap/slide_wrap.h"
|
||||||
|
#include "ui/ui_utility.h"
|
||||||
#include "base/random.h"
|
#include "base/random.h"
|
||||||
#include "base/weak_ptr.h"
|
#include "base/weak_ptr.h"
|
||||||
#include "api/api_chat_participants.h"
|
#include "api/api_chat_participants.h"
|
||||||
|
@ -169,11 +170,15 @@ void AddBotToGroupBoxController::requestExistingRights(
|
||||||
channel);
|
channel);
|
||||||
_existingRights = participant.rights().flags;
|
_existingRights = participant.rights().flags;
|
||||||
_existingRank = participant.rank();
|
_existingRank = participant.rank();
|
||||||
|
_promotedSince = participant.promotedSince();
|
||||||
|
_promotedBy = participant.by();
|
||||||
addBotToGroup(_existingRightsChannel);
|
addBotToGroup(_existingRightsChannel);
|
||||||
});
|
});
|
||||||
}).fail([=] {
|
}).fail([=] {
|
||||||
_existingRights = ChatAdminRights();
|
_existingRights = ChatAdminRights();
|
||||||
_existingRank = QString();
|
_existingRank = QString();
|
||||||
|
_promotedSince = 0;
|
||||||
|
_promotedBy = 0;
|
||||||
addBotToGroup(_existingRightsChannel);
|
addBotToGroup(_existingRightsChannel);
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
@ -190,6 +195,8 @@ void AddBotToGroupBoxController::addBotToGroup(not_null<PeerData*> chat) {
|
||||||
_existingRights = {};
|
_existingRights = {};
|
||||||
_existingRank = QString();
|
_existingRank = QString();
|
||||||
_existingRightsChannel = nullptr;
|
_existingRightsChannel = nullptr;
|
||||||
|
_promotedSince = 0;
|
||||||
|
_promotedBy = 0;
|
||||||
_bot->session().api().request(_existingRightsRequestId).cancel();
|
_bot->session().api().request(_existingRightsRequestId).cancel();
|
||||||
}
|
}
|
||||||
const auto requestedAddAdmin = (_scope == Scope::GroupAdmin)
|
const auto requestedAddAdmin = (_scope == Scope::GroupAdmin)
|
||||||
|
@ -240,9 +247,12 @@ void AddBotToGroupBoxController::addBotToGroup(not_null<PeerData*> chat) {
|
||||||
bot,
|
bot,
|
||||||
ChatAdminRightsInfo(rights),
|
ChatAdminRightsInfo(rights),
|
||||||
_existingRank,
|
_existingRank,
|
||||||
|
_promotedSince,
|
||||||
|
_promotedBy ? chat->owner().user(_promotedBy).get() : nullptr,
|
||||||
EditAdminBotFields{
|
EditAdminBotFields{
|
||||||
_token,
|
_token,
|
||||||
_existingRights.value_or(ChatAdminRights()) });
|
_existingRights.value_or(ChatAdminRights()),
|
||||||
|
});
|
||||||
box->setSaveCallback(saveCallback);
|
box->setSaveCallback(saveCallback);
|
||||||
controller->show(std::move(box));
|
controller->show(std::move(box));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -65,6 +65,8 @@ private:
|
||||||
mtpRequestId _existingRightsRequestId = 0;
|
mtpRequestId _existingRightsRequestId = 0;
|
||||||
std::optional<ChatAdminRights> _existingRights;
|
std::optional<ChatAdminRights> _existingRights;
|
||||||
QString _existingRank;
|
QString _existingRank;
|
||||||
|
TimeId _promotedSince = 0;
|
||||||
|
UserId _promotedBy = 0;
|
||||||
|
|
||||||
rpl::event_stream<not_null<PeerData*>> _groups;
|
rpl::event_stream<not_null<PeerData*>> _groups;
|
||||||
rpl::event_stream<not_null<PeerData*>> _channels;
|
rpl::event_stream<not_null<PeerData*>> _channels;
|
||||||
|
|
|
@ -1276,7 +1276,9 @@ void AddSpecialBoxController::showAdmin(
|
||||||
_peer,
|
_peer,
|
||||||
user,
|
user,
|
||||||
currentRights,
|
currentRights,
|
||||||
_additional.adminRank(user));
|
_additional.adminRank(user),
|
||||||
|
_additional.adminPromotedSince(user),
|
||||||
|
_additional.adminPromotedBy(user));
|
||||||
const auto show = delegate()->peerListUiShow();
|
const auto show = delegate()->peerListUiShow();
|
||||||
if (_additional.canAddOrEditAdmin(user)) {
|
if (_additional.canAddOrEditAdmin(user)) {
|
||||||
const auto done = crl::guard(this, [=](
|
const auto done = crl::guard(this, [=](
|
||||||
|
@ -1354,7 +1356,9 @@ void AddSpecialBoxController::showRestricted(
|
||||||
_peer,
|
_peer,
|
||||||
user,
|
user,
|
||||||
_additional.adminRights(user).has_value(),
|
_additional.adminRights(user).has_value(),
|
||||||
currentRights);
|
currentRights,
|
||||||
|
_additional.restrictedBy(user),
|
||||||
|
_additional.restrictedSince(user));
|
||||||
if (_additional.canRestrictParticipant(user)) {
|
if (_additional.canRestrictParticipant(user)) {
|
||||||
const auto done = crl::guard(this, [=](
|
const auto done = crl::guard(this, [=](
|
||||||
ChatRestrictionsInfo newRights) {
|
ChatRestrictionsInfo newRights) {
|
||||||
|
|
|
@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_forum_topic.h"
|
#include "data/data_forum_topic.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/stickers/data_custom_emoji.h"
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
|
#include "base/event_filter.h"
|
||||||
#include "base/random.h"
|
#include "base/random.h"
|
||||||
#include "base/qt_signal_producer.h"
|
#include "base/qt_signal_producer.h"
|
||||||
#include "chat_helpers/emoji_list_widget.h"
|
#include "chat_helpers/emoji_list_widget.h"
|
||||||
|
@ -482,6 +483,9 @@ void EditForumTopicBox(
|
||||||
state->defaultIcon.current().colorId,
|
state->defaultIcon.current().colorId,
|
||||||
};
|
};
|
||||||
}, title->lifetime());
|
}, title->lifetime());
|
||||||
|
title->submits() | rpl::start_with_next([box] {
|
||||||
|
box->triggerButton(0);
|
||||||
|
}, title->lifetime());
|
||||||
|
|
||||||
if (!topic || !topic->isGeneral()) {
|
if (!topic || !topic->isGeneral()) {
|
||||||
Ui::AddDividerText(top, tr::lng_forum_choose_title_and_icon());
|
Ui::AddDividerText(top, tr::lng_forum_choose_title_and_icon());
|
||||||
|
|
|
@ -16,6 +16,10 @@ struct TopicIconDescriptor;
|
||||||
enum class CustomEmojiSizeTag : uchar;
|
enum class CustomEmojiSizeTag : uchar;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
|
namespace Ui::Text {
|
||||||
|
class CustomEmoji;
|
||||||
|
} // namespace Ui::Text
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
class SessionController;
|
class SessionController;
|
||||||
} // namespace Window
|
} // namespace Window
|
||||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "ui/controls/userpic_button.h"
|
#include "ui/controls/userpic_button.h"
|
||||||
|
#include "ui/vertical_list.h"
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
#include "ui/wrap/padding_wrap.h"
|
#include "ui/wrap/padding_wrap.h"
|
||||||
#include "ui/wrap/slide_wrap.h"
|
#include "ui/wrap/slide_wrap.h"
|
||||||
|
@ -63,6 +64,10 @@ public:
|
||||||
template <typename Widget>
|
template <typename Widget>
|
||||||
Widget *addControl(object_ptr<Widget> widget, QMargins margin);
|
Widget *addControl(object_ptr<Widget> widget, QMargins margin);
|
||||||
|
|
||||||
|
[[nodiscard]] not_null<Ui::VerticalLayout*> verticalLayout() const {
|
||||||
|
return _rows;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int resizeGetHeight(int newWidth) override;
|
int resizeGetHeight(int newWidth) override;
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
@ -164,6 +169,10 @@ EditParticipantBox::EditParticipantBox(
|
||||||
, _hasAdminRights(hasAdminRights) {
|
, _hasAdminRights(hasAdminRights) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
not_null<Ui::VerticalLayout*> EditParticipantBox::verticalLayout() const {
|
||||||
|
return _inner->verticalLayout();
|
||||||
|
}
|
||||||
|
|
||||||
void EditParticipantBox::prepare() {
|
void EditParticipantBox::prepare() {
|
||||||
_inner = setInnerWidget(object_ptr<Inner>(
|
_inner = setInnerWidget(object_ptr<Inner>(
|
||||||
this,
|
this,
|
||||||
|
@ -197,6 +206,8 @@ EditAdminBox::EditAdminBox(
|
||||||
not_null<UserData*> user,
|
not_null<UserData*> user,
|
||||||
ChatAdminRightsInfo rights,
|
ChatAdminRightsInfo rights,
|
||||||
const QString &rank,
|
const QString &rank,
|
||||||
|
TimeId promotedSince,
|
||||||
|
UserData *by,
|
||||||
std::optional<EditAdminBotFields> addingBot)
|
std::optional<EditAdminBotFields> addingBot)
|
||||||
: EditParticipantBox(
|
: EditParticipantBox(
|
||||||
nullptr,
|
nullptr,
|
||||||
|
@ -205,6 +216,8 @@ EditAdminBox::EditAdminBox(
|
||||||
(rights.flags != 0))
|
(rights.flags != 0))
|
||||||
, _oldRights(rights)
|
, _oldRights(rights)
|
||||||
, _oldRank(rank)
|
, _oldRank(rank)
|
||||||
|
, _promotedSince(promotedSince)
|
||||||
|
, _by(by)
|
||||||
, _addingBot(std::move(addingBot)) {
|
, _addingBot(std::move(addingBot)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,9 +292,26 @@ void EditAdminBox::prepare() {
|
||||||
object_ptr<Ui::VerticalLayout>(this)));
|
object_ptr<Ui::VerticalLayout>(this)));
|
||||||
const auto inner = _adminControlsWrap->entity();
|
const auto inner = _adminControlsWrap->entity();
|
||||||
|
|
||||||
inner->add(
|
if (_promotedSince) {
|
||||||
object_ptr<Ui::BoxContentDivider>(inner),
|
const auto parsed = base::unixtime::parse(_promotedSince);
|
||||||
st::rightsDividerMargin);
|
const auto label = Ui::AddDividerText(
|
||||||
|
inner,
|
||||||
|
tr::lng_rights_about_by(
|
||||||
|
lt_user,
|
||||||
|
rpl::single(_by
|
||||||
|
? Ui::Text::Link(_by->name(), 1)
|
||||||
|
: TextWithEntities{ QString::fromUtf8("\U0001F47B") }),
|
||||||
|
lt_date,
|
||||||
|
rpl::single(TextWithEntities{ langDateTimeFull(parsed) }),
|
||||||
|
Ui::Text::WithEntities));
|
||||||
|
if (_by) {
|
||||||
|
label->setLink(1, _by->createOpenLink());
|
||||||
|
}
|
||||||
|
Ui::AddSkip(inner);
|
||||||
|
} else {
|
||||||
|
Ui::AddDivider(inner);
|
||||||
|
Ui::AddSkip(inner);
|
||||||
|
}
|
||||||
|
|
||||||
const auto chat = peer()->asChat();
|
const auto chat = peer()->asChat();
|
||||||
const auto channel = peer()->asChannel();
|
const auto channel = peer()->asChannel();
|
||||||
|
@ -335,9 +365,9 @@ void EditAdminBox::prepare() {
|
||||||
.isForum = peer()->isForum(),
|
.isForum = peer()->isForum(),
|
||||||
.anyoneCanAddMembers = anyoneCanAddMembers,
|
.anyoneCanAddMembers = anyoneCanAddMembers,
|
||||||
};
|
};
|
||||||
|
Ui::AddSubsectionTitle(inner, tr::lng_rights_edit_admin_header());
|
||||||
auto [checkboxes, getChecked, changes] = CreateEditAdminRights(
|
auto [checkboxes, getChecked, changes] = CreateEditAdminRights(
|
||||||
inner,
|
inner,
|
||||||
tr::lng_rights_edit_admin_header(),
|
|
||||||
prepareFlags,
|
prepareFlags,
|
||||||
disabledMessages,
|
disabledMessages,
|
||||||
options);
|
options);
|
||||||
|
@ -348,17 +378,47 @@ void EditAdminBox::prepare() {
|
||||||
) | rpl::then(std::move(
|
) | rpl::then(std::move(
|
||||||
changes
|
changes
|
||||||
));
|
));
|
||||||
_aboutAddAdmins = inner->add(
|
|
||||||
object_ptr<Ui::FlatLabel>(inner, st::boxDividerLabel),
|
const auto hasRank = canSave() && (chat || channel->isMegagroup());
|
||||||
st::rightsAboutMargin);
|
|
||||||
rpl::duplicate(
|
{
|
||||||
selectedFlags
|
const auto aboutAddAdminsInner = inner->add(
|
||||||
) | rpl::map(
|
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||||
(_1 & Flag::AddAdmins) != 0
|
inner,
|
||||||
) | rpl::distinct_until_changed(
|
object_ptr<Ui::VerticalLayout>(inner)));
|
||||||
) | rpl::start_with_next([=](bool checked) {
|
const auto emptyAboutAddAdminsInner = inner->add(
|
||||||
refreshAboutAddAdminsText(checked);
|
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||||
}, lifetime());
|
inner,
|
||||||
|
object_ptr<Ui::VerticalLayout>(inner)));
|
||||||
|
aboutAddAdminsInner->toggle(false, anim::type::instant);
|
||||||
|
emptyAboutAddAdminsInner->toggle(false, anim::type::instant);
|
||||||
|
Ui::AddSkip(emptyAboutAddAdminsInner->entity());
|
||||||
|
if (hasRank) {
|
||||||
|
Ui::AddDivider(emptyAboutAddAdminsInner->entity());
|
||||||
|
Ui::AddSkip(emptyAboutAddAdminsInner->entity());
|
||||||
|
}
|
||||||
|
Ui::AddSkip(aboutAddAdminsInner->entity());
|
||||||
|
Ui::AddDividerText(
|
||||||
|
aboutAddAdminsInner->entity(),
|
||||||
|
rpl::duplicate(
|
||||||
|
selectedFlags
|
||||||
|
) | rpl::map(
|
||||||
|
(_1 & Flag::AddAdmins) != 0
|
||||||
|
) | rpl::distinct_until_changed(
|
||||||
|
) | rpl::map([=](bool canAddAdmins) -> rpl::producer<QString> {
|
||||||
|
const auto empty = (amCreator() && user()->isSelf());
|
||||||
|
aboutAddAdminsInner->toggle(!empty, anim::type::instant);
|
||||||
|
emptyAboutAddAdminsInner->toggle(empty, anim::type::instant);
|
||||||
|
if (empty) {
|
||||||
|
return rpl::single(QString());
|
||||||
|
} else if (!canSave()) {
|
||||||
|
return tr::lng_rights_about_admin_cant_edit();
|
||||||
|
} else if (canAddAdmins) {
|
||||||
|
return tr::lng_rights_about_add_admins_yes();
|
||||||
|
}
|
||||||
|
return tr::lng_rights_about_add_admins_no();
|
||||||
|
}) | rpl::flatten_latest());
|
||||||
|
}
|
||||||
|
|
||||||
if (canTransferOwnership()) {
|
if (canTransferOwnership()) {
|
||||||
const auto allFlags = AdminRightsForOwnershipTransfer(options);
|
const auto allFlags = AdminRightsForOwnershipTransfer(options);
|
||||||
|
@ -373,9 +433,7 @@ void EditAdminBox::prepare() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canSave()) {
|
if (canSave()) {
|
||||||
_rank = (chat || channel->isMegagroup())
|
_rank = hasRank ? addRankInput(inner).get() : nullptr;
|
||||||
? addRankInput(inner).get()
|
|
||||||
: nullptr;
|
|
||||||
_finishSave = [=, value = getChecked] {
|
_finishSave = [=, value = getChecked] {
|
||||||
const auto newFlags = (value() | ChatAdminRight::Other)
|
const auto newFlags = (value() | ChatAdminRight::Other)
|
||||||
& ((!channel || channel->amCreator())
|
& ((!channel || channel->amCreator())
|
||||||
|
@ -441,9 +499,7 @@ void EditAdminBox::refreshButtons() {
|
||||||
|
|
||||||
not_null<Ui::InputField*> EditAdminBox::addRankInput(
|
not_null<Ui::InputField*> EditAdminBox::addRankInput(
|
||||||
not_null<Ui::VerticalLayout*> container) {
|
not_null<Ui::VerticalLayout*> container) {
|
||||||
container->add(
|
// Ui::AddDivider(container);
|
||||||
object_ptr<Ui::BoxContentDivider>(container),
|
|
||||||
st::rightsRankMargin);
|
|
||||||
|
|
||||||
container->add(
|
container->add(
|
||||||
object_ptr<Ui::FlatLabel>(
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
@ -480,14 +536,13 @@ not_null<Ui::InputField*> EditAdminBox::addRankInput(
|
||||||
}
|
}
|
||||||
}, result->lifetime());
|
}, result->lifetime());
|
||||||
|
|
||||||
container->add(
|
Ui::AddSkip(container);
|
||||||
object_ptr<Ui::FlatLabel>(
|
Ui::AddDividerText(
|
||||||
container,
|
container,
|
||||||
tr::lng_rights_edit_admin_rank_about(
|
tr::lng_rights_edit_admin_rank_about(
|
||||||
lt_title,
|
lt_title,
|
||||||
(isOwner ? tr::lng_owner_badge : tr::lng_admin_badge)()),
|
(isOwner ? tr::lng_owner_badge : tr::lng_admin_badge)()));
|
||||||
st::boxDividerLabel),
|
Ui::AddSkip(container);
|
||||||
st::rightsAboutMargin);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -681,27 +736,18 @@ void EditAdminBox::sendTransferRequestFrom(
|
||||||
})).handleFloodErrors().send();
|
})).handleFloodErrors().send();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditAdminBox::refreshAboutAddAdminsText(bool canAddAdmins) {
|
|
||||||
_aboutAddAdmins->setText([&] {
|
|
||||||
if (amCreator() && user()->isSelf()) {
|
|
||||||
return QString();
|
|
||||||
} else if (!canSave()) {
|
|
||||||
return tr::lng_rights_about_admin_cant_edit(tr::now);
|
|
||||||
} else if (canAddAdmins) {
|
|
||||||
return tr::lng_rights_about_add_admins_yes(tr::now);
|
|
||||||
}
|
|
||||||
return tr::lng_rights_about_add_admins_no(tr::now);
|
|
||||||
}());
|
|
||||||
}
|
|
||||||
|
|
||||||
EditRestrictedBox::EditRestrictedBox(
|
EditRestrictedBox::EditRestrictedBox(
|
||||||
QWidget*,
|
QWidget*,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
not_null<UserData*> user,
|
not_null<UserData*> user,
|
||||||
bool hasAdminRights,
|
bool hasAdminRights,
|
||||||
ChatRestrictionsInfo rights)
|
ChatRestrictionsInfo rights,
|
||||||
|
UserData *by,
|
||||||
|
TimeId since)
|
||||||
: EditParticipantBox(nullptr, peer, user, hasAdminRights)
|
: EditParticipantBox(nullptr, peer, user, hasAdminRights)
|
||||||
, _oldRights(rights) {
|
, _oldRights(rights)
|
||||||
|
, _by(by)
|
||||||
|
, _since(since) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditRestrictedBox::prepare() {
|
void EditRestrictedBox::prepare() {
|
||||||
|
@ -712,9 +758,8 @@ void EditRestrictedBox::prepare() {
|
||||||
|
|
||||||
setTitle(tr::lng_rights_user_restrictions());
|
setTitle(tr::lng_rights_user_restrictions());
|
||||||
|
|
||||||
addControl(
|
Ui::AddDivider(verticalLayout());
|
||||||
object_ptr<Ui::BoxContentDivider>(this),
|
Ui::AddSkip(verticalLayout());
|
||||||
st::rightsDividerMargin);
|
|
||||||
|
|
||||||
const auto chat = peer()->asChat();
|
const auto chat = peer()->asChat();
|
||||||
const auto channel = peer()->asChannel();
|
const auto channel = peer()->asChannel();
|
||||||
|
@ -749,16 +794,20 @@ void EditRestrictedBox::prepare() {
|
||||||
return result;
|
return result;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
Ui::AddSubsectionTitle(
|
||||||
|
verticalLayout(),
|
||||||
|
tr::lng_rights_user_restrictions_header());
|
||||||
auto [checkboxes, getRestrictions, changes] = CreateEditRestrictions(
|
auto [checkboxes, getRestrictions, changes] = CreateEditRestrictions(
|
||||||
this,
|
this,
|
||||||
tr::lng_rights_user_restrictions_header(),
|
|
||||||
prepareFlags,
|
prepareFlags,
|
||||||
disabledMessages,
|
disabledMessages,
|
||||||
{ .isForum = peer()->isForum() });
|
{ .isForum = peer()->isForum() });
|
||||||
addControl(std::move(checkboxes), QMargins());
|
addControl(std::move(checkboxes), QMargins());
|
||||||
|
|
||||||
_until = prepareRights.until;
|
_until = prepareRights.until;
|
||||||
addControl(object_ptr<Ui::BoxContentDivider>(this), st::rightsUntilMargin);
|
addControl(
|
||||||
|
object_ptr<Ui::FixedHeightWidget>(this, st::defaultVerticalListSkip));
|
||||||
|
Ui::AddDivider(verticalLayout());
|
||||||
addControl(
|
addControl(
|
||||||
object_ptr<Ui::FlatLabel>(
|
object_ptr<Ui::FlatLabel>(
|
||||||
this,
|
this,
|
||||||
|
@ -773,6 +822,29 @@ void EditRestrictedBox::prepare() {
|
||||||
// tr::lng_rights_chat_banned_block(tr::now),
|
// tr::lng_rights_chat_banned_block(tr::now),
|
||||||
// st::boxLinkButton));
|
// st::boxLinkButton));
|
||||||
|
|
||||||
|
if (_since) {
|
||||||
|
const auto parsed = base::unixtime::parse(_since);
|
||||||
|
const auto inner = addControl(object_ptr<Ui::VerticalLayout>(this));
|
||||||
|
const auto isBanned = (_oldRights.flags
|
||||||
|
& ChatRestriction::ViewMessages);
|
||||||
|
Ui::AddSkip(inner);
|
||||||
|
const auto label = Ui::AddDividerText(
|
||||||
|
inner,
|
||||||
|
(isBanned
|
||||||
|
? tr::lng_rights_chat_banned_by
|
||||||
|
: tr::lng_rights_chat_restricted_by)(
|
||||||
|
lt_user,
|
||||||
|
rpl::single(_by
|
||||||
|
? Ui::Text::Link(_by->name(), 1)
|
||||||
|
: TextWithEntities{ QString::fromUtf8("\U0001F47B") }),
|
||||||
|
lt_date,
|
||||||
|
rpl::single(TextWithEntities{ langDateTimeFull(parsed) }),
|
||||||
|
Ui::Text::WithEntities));
|
||||||
|
if (_by) {
|
||||||
|
label->setLink(1, _by->createOpenLink());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (canSave()) {
|
if (canSave()) {
|
||||||
const auto save = [=, value = getRestrictions] {
|
const auto save = [=, value = getRestrictions] {
|
||||||
if (!_saveCallback) {
|
if (!_saveCallback) {
|
||||||
|
|
|
@ -36,6 +36,8 @@ public:
|
||||||
not_null<UserData*> user,
|
not_null<UserData*> user,
|
||||||
bool hasAdminRights);
|
bool hasAdminRights);
|
||||||
|
|
||||||
|
[[nodiscard]] not_null<Ui::VerticalLayout*> verticalLayout() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void prepare() override;
|
void prepare() override;
|
||||||
|
|
||||||
|
@ -77,6 +79,8 @@ public:
|
||||||
not_null<UserData*> user,
|
not_null<UserData*> user,
|
||||||
ChatAdminRightsInfo rights,
|
ChatAdminRightsInfo rights,
|
||||||
const QString &rank,
|
const QString &rank,
|
||||||
|
TimeId promotedSince,
|
||||||
|
UserData *by,
|
||||||
std::optional<EditAdminBotFields> addingBot = {});
|
std::optional<EditAdminBotFields> addingBot = {});
|
||||||
|
|
||||||
void setSaveCallback(
|
void setSaveCallback(
|
||||||
|
@ -108,7 +112,6 @@ private:
|
||||||
}
|
}
|
||||||
void finishAddAdmin();
|
void finishAddAdmin();
|
||||||
void refreshButtons();
|
void refreshButtons();
|
||||||
void refreshAboutAddAdminsText(bool canAddAdmins);
|
|
||||||
bool canTransferOwnership() const;
|
bool canTransferOwnership() const;
|
||||||
not_null<Ui::SlideWrap<Ui::RpWidget>*> setupTransferButton(
|
not_null<Ui::SlideWrap<Ui::RpWidget>*> setupTransferButton(
|
||||||
not_null<Ui::VerticalLayout*> container,
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
@ -125,11 +128,12 @@ private:
|
||||||
Ui::Checkbox *_addAsAdmin = nullptr;
|
Ui::Checkbox *_addAsAdmin = nullptr;
|
||||||
Ui::SlideWrap<Ui::VerticalLayout> *_adminControlsWrap = nullptr;
|
Ui::SlideWrap<Ui::VerticalLayout> *_adminControlsWrap = nullptr;
|
||||||
Ui::InputField *_rank = nullptr;
|
Ui::InputField *_rank = nullptr;
|
||||||
QPointer<Ui::FlatLabel> _aboutAddAdmins;
|
|
||||||
mtpRequestId _checkTransferRequestId = 0;
|
mtpRequestId _checkTransferRequestId = 0;
|
||||||
mtpRequestId _transferRequestId = 0;
|
mtpRequestId _transferRequestId = 0;
|
||||||
Fn<void()> _save, _finishSave;
|
Fn<void()> _save, _finishSave;
|
||||||
|
|
||||||
|
TimeId _promotedSince = 0;
|
||||||
|
UserData *_by = nullptr;
|
||||||
std::optional<EditAdminBotFields> _addingBot;
|
std::optional<EditAdminBotFields> _addingBot;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -144,7 +148,9 @@ public:
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
not_null<UserData*> user,
|
not_null<UserData*> user,
|
||||||
bool hasAdminRights,
|
bool hasAdminRights,
|
||||||
ChatRestrictionsInfo rights);
|
ChatRestrictionsInfo rights,
|
||||||
|
UserData *by,
|
||||||
|
TimeId since);
|
||||||
|
|
||||||
void setSaveCallback(
|
void setSaveCallback(
|
||||||
Fn<void(ChatRestrictionsInfo, ChatRestrictionsInfo)> callback) {
|
Fn<void(ChatRestrictionsInfo, ChatRestrictionsInfo)> callback) {
|
||||||
|
@ -168,6 +174,8 @@ private:
|
||||||
TimeId getRealUntilValue() const;
|
TimeId getRealUntilValue() const;
|
||||||
|
|
||||||
const ChatRestrictionsInfo _oldRights;
|
const ChatRestrictionsInfo _oldRights;
|
||||||
|
UserData *_by = nullptr;
|
||||||
|
TimeId _since = 0;
|
||||||
TimeId _until = 0;
|
TimeId _until = 0;
|
||||||
Fn<void(ChatRestrictionsInfo, ChatRestrictionsInfo)> _saveCallback;
|
Fn<void(ChatRestrictionsInfo, ChatRestrictionsInfo)> _saveCallback;
|
||||||
|
|
||||||
|
|
|
@ -387,6 +387,24 @@ QString ParticipantsAdditionalData::adminRank(
|
||||||
return (i != end(_adminRanks)) ? i->second : QString();
|
return (i != end(_adminRanks)) ? i->second : QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimeId ParticipantsAdditionalData::adminPromotedSince(
|
||||||
|
not_null<UserData*> user) const {
|
||||||
|
const auto i = _adminPromotedSince.find(user);
|
||||||
|
return (i != end(_adminPromotedSince)) ? i->second : TimeId(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeId ParticipantsAdditionalData::restrictedSince(
|
||||||
|
not_null<PeerData*> peer) const {
|
||||||
|
const auto i = _restrictedSince.find(peer);
|
||||||
|
return (i != end(_restrictedSince)) ? i->second : TimeId(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeId ParticipantsAdditionalData::memberSince(
|
||||||
|
not_null<UserData*> user) const {
|
||||||
|
const auto i = _memberSince.find(user);
|
||||||
|
return (i != end(_memberSince)) ? i->second : TimeId(0);
|
||||||
|
}
|
||||||
|
|
||||||
auto ParticipantsAdditionalData::restrictedRights(
|
auto ParticipantsAdditionalData::restrictedRights(
|
||||||
not_null<PeerData*> participant) const
|
not_null<PeerData*> participant) const
|
||||||
-> std::optional<ChatRestrictionsInfo> {
|
-> std::optional<ChatRestrictionsInfo> {
|
||||||
|
@ -689,6 +707,11 @@ UserData *ParticipantsAdditionalData::applyAdmin(
|
||||||
} else {
|
} else {
|
||||||
_adminRanks.remove(user);
|
_adminRanks.remove(user);
|
||||||
}
|
}
|
||||||
|
if (data.promotedSince()) {
|
||||||
|
_adminPromotedSince[user] = data.promotedSince();
|
||||||
|
} else {
|
||||||
|
_adminPromotedSince.remove(user);
|
||||||
|
}
|
||||||
if (const auto by = _peer->owner().userLoaded(data.by())) {
|
if (const auto by = _peer->owner().userLoaded(data.by())) {
|
||||||
const auto i = _adminPromotedBy.find(user);
|
const auto i = _adminPromotedBy.find(user);
|
||||||
if (i == _adminPromotedBy.end()) {
|
if (i == _adminPromotedBy.end()) {
|
||||||
|
@ -741,6 +764,11 @@ PeerData *ParticipantsAdditionalData::applyBanned(
|
||||||
} else {
|
} else {
|
||||||
_kicked.erase(participant);
|
_kicked.erase(participant);
|
||||||
}
|
}
|
||||||
|
if (data.restrictedSince()) {
|
||||||
|
_restrictedSince[participant] = data.restrictedSince();
|
||||||
|
} else {
|
||||||
|
_restrictedSince.remove(participant);
|
||||||
|
}
|
||||||
_restrictedRights[participant] = data.restrictions();
|
_restrictedRights[participant] = data.restrictions();
|
||||||
if (const auto by = _peer->owner().userLoaded(data.by())) {
|
if (const auto by = _peer->owner().userLoaded(data.by())) {
|
||||||
const auto i = _restrictedBy.find(participant);
|
const auto i = _restrictedBy.find(participant);
|
||||||
|
@ -1720,7 +1748,9 @@ void ParticipantsBoxController::showAdmin(not_null<UserData*> user) {
|
||||||
_peer,
|
_peer,
|
||||||
user,
|
user,
|
||||||
currentRights,
|
currentRights,
|
||||||
_additional.adminRank(user));
|
_additional.adminRank(user),
|
||||||
|
_additional.adminPromotedSince(user),
|
||||||
|
_additional.adminPromotedBy(user));
|
||||||
if (_additional.canAddOrEditAdmin(user)) {
|
if (_additional.canAddOrEditAdmin(user)) {
|
||||||
const auto done = crl::guard(this, [=](
|
const auto done = crl::guard(this, [=](
|
||||||
ChatAdminRightsInfo newRights,
|
ChatAdminRightsInfo newRights,
|
||||||
|
@ -1776,7 +1806,9 @@ void ParticipantsBoxController::showRestricted(not_null<UserData*> user) {
|
||||||
_peer,
|
_peer,
|
||||||
user,
|
user,
|
||||||
hasAdminRights,
|
hasAdminRights,
|
||||||
currentRights);
|
currentRights,
|
||||||
|
_additional.restrictedBy(user),
|
||||||
|
_additional.restrictedSince(user));
|
||||||
if (_additional.canRestrictParticipant(user)) {
|
if (_additional.canRestrictParticipant(user)) {
|
||||||
const auto done = crl::guard(this, [=](
|
const auto done = crl::guard(this, [=](
|
||||||
ChatRestrictionsInfo newRights) {
|
ChatRestrictionsInfo newRights) {
|
||||||
|
|
|
@ -106,14 +106,19 @@ public:
|
||||||
not_null<PeerData*> participant) const;
|
not_null<PeerData*> participant) const;
|
||||||
[[nodiscard]] std::optional<ChatAdminRightsInfo> adminRights(
|
[[nodiscard]] std::optional<ChatAdminRightsInfo> adminRights(
|
||||||
not_null<UserData*> user) const;
|
not_null<UserData*> user) const;
|
||||||
QString adminRank(not_null<UserData*> user) const;
|
[[nodiscard]] QString adminRank(not_null<UserData*> user) const;
|
||||||
[[nodiscard]] std::optional<ChatRestrictionsInfo> restrictedRights(
|
[[nodiscard]] std::optional<ChatRestrictionsInfo> restrictedRights(
|
||||||
not_null<PeerData*> participant) const;
|
not_null<PeerData*> participant) const;
|
||||||
[[nodiscard]] bool isCreator(not_null<UserData*> user) const;
|
[[nodiscard]] bool isCreator(not_null<UserData*> user) const;
|
||||||
[[nodiscard]] bool isExternal(not_null<PeerData*> participant) const;
|
[[nodiscard]] bool isExternal(not_null<PeerData*> participant) const;
|
||||||
[[nodiscard]] bool isKicked(not_null<PeerData*> participant) const;
|
[[nodiscard]] bool isKicked(not_null<PeerData*> participant) const;
|
||||||
[[nodiscard]] UserData *adminPromotedBy(not_null<UserData*> user) const;
|
[[nodiscard]] UserData *adminPromotedBy(not_null<UserData*> user) const;
|
||||||
[[nodiscard]] UserData *restrictedBy(not_null<PeerData*> participant) const;
|
[[nodiscard]] UserData *restrictedBy(
|
||||||
|
not_null<PeerData*> participant) const;
|
||||||
|
|
||||||
|
[[nodiscard]] TimeId adminPromotedSince(not_null<UserData*>) const;
|
||||||
|
[[nodiscard]] TimeId restrictedSince(not_null<PeerData*>) const;
|
||||||
|
[[nodiscard]] TimeId memberSince(not_null<UserData*>) const;
|
||||||
|
|
||||||
void migrate(not_null<ChatData*> chat, not_null<ChannelData*> channel);
|
void migrate(not_null<ChatData*> chat, not_null<ChannelData*> channel);
|
||||||
|
|
||||||
|
@ -144,6 +149,9 @@ private:
|
||||||
// Data for channels.
|
// Data for channels.
|
||||||
base::flat_map<not_null<UserData*>, ChatAdminRightsInfo> _adminRights;
|
base::flat_map<not_null<UserData*>, ChatAdminRightsInfo> _adminRights;
|
||||||
base::flat_map<not_null<UserData*>, QString> _adminRanks;
|
base::flat_map<not_null<UserData*>, QString> _adminRanks;
|
||||||
|
base::flat_map<not_null<UserData*>, TimeId> _adminPromotedSince;
|
||||||
|
base::flat_map<not_null<PeerData*>, TimeId> _restrictedSince;
|
||||||
|
base::flat_map<not_null<UserData*>, TimeId> _memberSince;
|
||||||
base::flat_set<not_null<UserData*>> _adminCanEdit;
|
base::flat_set<not_null<UserData*>> _adminCanEdit;
|
||||||
base::flat_map<not_null<UserData*>, not_null<UserData*>> _adminPromotedBy;
|
base::flat_map<not_null<UserData*>, not_null<UserData*>> _adminPromotedBy;
|
||||||
std::map<not_null<PeerData*>, ChatRestrictionsInfo> _restrictedRights;
|
std::map<not_null<PeerData*>, ChatRestrictionsInfo> _restrictedRights;
|
||||||
|
|
|
@ -67,6 +67,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/wrap/padding_wrap.h"
|
#include "ui/wrap/padding_wrap.h"
|
||||||
#include "ui/wrap/slide_wrap.h"
|
#include "ui/wrap/slide_wrap.h"
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
|
#include "ui/ui_utility.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "api/api_invite_links.h"
|
#include "api/api_invite_links.h"
|
||||||
#include "styles/style_chat_helpers.h"
|
#include "styles/style_chat_helpers.h"
|
||||||
|
|
|
@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/boxes/confirm_box.h"
|
#include "ui/boxes/confirm_box.h"
|
||||||
#include "ui/boxes/edit_invite_link.h"
|
#include "ui/boxes/edit_invite_link.h"
|
||||||
#include "ui/boxes/edit_invite_link_session.h"
|
#include "ui/boxes/edit_invite_link_session.h"
|
||||||
|
#include "ui/boxes/peer_qr_box.h"
|
||||||
#include "ui/controls/invite_link_buttons.h"
|
#include "ui/controls/invite_link_buttons.h"
|
||||||
#include "ui/controls/invite_link_label.h"
|
#include "ui/controls/invite_link_label.h"
|
||||||
#include "ui/controls/userpic_button.h"
|
#include "ui/controls/userpic_button.h"
|
||||||
|
@ -64,8 +65,8 @@ namespace {
|
||||||
|
|
||||||
constexpr auto kFirstPage = 20;
|
constexpr auto kFirstPage = 20;
|
||||||
constexpr auto kPerPage = 100;
|
constexpr auto kPerPage = 100;
|
||||||
constexpr auto kShareQrSize = 768;
|
// constexpr auto kShareQrSize = 768;
|
||||||
constexpr auto kShareQrPadding = 16;
|
// constexpr auto kShareQrPadding = 16;
|
||||||
|
|
||||||
using LinkData = Api::InviteLink;
|
using LinkData = Api::InviteLink;
|
||||||
|
|
||||||
|
@ -282,6 +283,8 @@ private:
|
||||||
return updated.link.isEmpty() || (!revoked && updated.revoked);
|
return updated.link.isEmpty() || (!revoked && updated.revoked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
QImage QrExact(const Qr::Data &data, int pixel, QColor color) {
|
QImage QrExact(const Qr::Data &data, int pixel, QColor color) {
|
||||||
const auto image = [](int size) {
|
const auto image = [](int size) {
|
||||||
auto result = QImage(
|
auto result = QImage(
|
||||||
|
@ -383,6 +386,8 @@ void QrBox(
|
||||||
box->addLeftButton(tr::lng_group_invite_context_copy(), copyCallback);
|
box->addLeftButton(tr::lng_group_invite_context_copy(), copyCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
Controller::Controller(
|
Controller::Controller(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
not_null<UserData*> admin,
|
not_null<UserData*> admin,
|
||||||
|
@ -421,6 +426,7 @@ void Controller::addHeaderBlock(not_null<Ui::VerticalLayout*> container) {
|
||||||
});
|
});
|
||||||
const auto getLinkQr = crl::guard(weak, [=] {
|
const auto getLinkQr = crl::guard(weak, [=] {
|
||||||
delegate()->peerListUiShow()->showBox(InviteLinkQrBox(
|
delegate()->peerListUiShow()->showBox(InviteLinkQrBox(
|
||||||
|
_peer,
|
||||||
link,
|
link,
|
||||||
tr::lng_group_invite_qr_title(),
|
tr::lng_group_invite_qr_title(),
|
||||||
tr::lng_group_invite_qr_about()));
|
tr::lng_group_invite_qr_about()));
|
||||||
|
@ -1253,6 +1259,7 @@ void AddPermanentLinkBlock(
|
||||||
const auto getLinkQr = crl::guard(weak, [=] {
|
const auto getLinkQr = crl::guard(weak, [=] {
|
||||||
if (const auto current = value->current(); !current.link.isEmpty()) {
|
if (const auto current = value->current(); !current.link.isEmpty()) {
|
||||||
show->showBox(InviteLinkQrBox(
|
show->showBox(InviteLinkQrBox(
|
||||||
|
peer,
|
||||||
current.link,
|
current.link,
|
||||||
tr::lng_group_invite_qr_title(),
|
tr::lng_group_invite_qr_title(),
|
||||||
tr::lng_group_invite_qr_about()));
|
tr::lng_group_invite_qr_about()));
|
||||||
|
@ -1510,16 +1517,14 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
|
||||||
}
|
}
|
||||||
|
|
||||||
object_ptr<Ui::BoxContent> InviteLinkQrBox(
|
object_ptr<Ui::BoxContent> InviteLinkQrBox(
|
||||||
|
PeerData *peer,
|
||||||
const QString &link,
|
const QString &link,
|
||||||
rpl::producer<QString> title,
|
rpl::producer<QString> title,
|
||||||
rpl::producer<QString> about) {
|
rpl::producer<QString> about) {
|
||||||
return Box(QrBox, link, std::move(title), std::move(about), [=](
|
return Box([=, t = std::move(title), a = std::move(about)](
|
||||||
const QImage &image,
|
not_null<Ui::GenericBox*> box) {
|
||||||
std::shared_ptr<Ui::Show> show) {
|
Ui::FillPeerQrBox(box, peer, link, std::move(a));
|
||||||
auto mime = std::make_unique<QMimeData>();
|
box->setTitle(std::move(t));
|
||||||
mime->setImageData(image);
|
|
||||||
QGuiApplication::clipboard()->setMimeData(mime.release());
|
|
||||||
show->showToast(tr::lng_group_invite_qr_copied(tr::now));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ void CopyInviteLink(std::shared_ptr<Ui::Show> show, const QString &link);
|
||||||
const QString &link,
|
const QString &link,
|
||||||
const QString &copied = {});
|
const QString &copied = {});
|
||||||
[[nodiscard]] object_ptr<Ui::BoxContent> InviteLinkQrBox(
|
[[nodiscard]] object_ptr<Ui::BoxContent> InviteLinkQrBox(
|
||||||
|
PeerData *peer,
|
||||||
const QString &link,
|
const QString &link,
|
||||||
rpl::producer<QString> title,
|
rpl::producer<QString> title,
|
||||||
rpl::producer<QString> about);
|
rpl::producer<QString> about);
|
||||||
|
|
|
@ -587,6 +587,7 @@ base::unique_qptr<Ui::PopupMenu> LinksController::createRowContextMenu(
|
||||||
}, &st::menuIconShare);
|
}, &st::menuIconShare);
|
||||||
result->addAction(tr::lng_group_invite_context_qr(tr::now), [=] {
|
result->addAction(tr::lng_group_invite_context_qr(tr::now), [=] {
|
||||||
delegate()->peerListUiShow()->showBox(InviteLinkQrBox(
|
delegate()->peerListUiShow()->showBox(InviteLinkQrBox(
|
||||||
|
nullptr,
|
||||||
link,
|
link,
|
||||||
tr::lng_group_invite_qr_title(),
|
tr::lng_group_invite_qr_title(),
|
||||||
tr::lng_group_invite_qr_about()));
|
tr::lng_group_invite_qr_about()));
|
||||||
|
@ -734,7 +735,7 @@ void LinksController::rowPaintIcon(
|
||||||
} else {
|
} else {
|
||||||
(color == Color::Revoked
|
(color == Color::Revoked
|
||||||
? st::inviteLinkRevokedIcon
|
? st::inviteLinkRevokedIcon
|
||||||
: st::inviteLinkIcon).paintInCenter(p, { 0, 0, inner, inner });
|
: st::inviteLinkIcon).paintInCenter(p, Rect(Size(inner)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.drawImage(x + skip, y + skip, icon);
|
p.drawImage(x + skip, y + skip, icon);
|
||||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "boxes/peers/edit_peer_permissions_box.h"
|
#include "boxes/peers/edit_peer_permissions_box.h"
|
||||||
|
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
#include "history/admin_log/history_admin_log_filter.h"
|
||||||
#include "core/ui_integration.h"
|
#include "core/ui_integration.h"
|
||||||
#include "data/stickers/data_custom_emoji.h"
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
|
@ -18,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
#include "ui/layers/generic_box.h"
|
#include "ui/layers/generic_box.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
|
#include "ui/vertical_list.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
#include "ui/widgets/checkbox.h"
|
#include "ui/widgets/checkbox.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
|
@ -55,6 +57,11 @@ constexpr auto kForceDisableTooltipDuration = 3 * crl::time(1000);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto Dependencies(AdminLog::FilterValue::Flags) {
|
||||||
|
using Flag = AdminLog::FilterValue::Flag;
|
||||||
|
return std::vector<std::pair<Flag, Flag>>{};
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] auto NestedRestrictionLabelsList(
|
[[nodiscard]] auto NestedRestrictionLabelsList(
|
||||||
Data::RestrictionsSetOptions options)
|
Data::RestrictionsSetOptions options)
|
||||||
-> std::vector<NestedEditFlagsLabels<ChatRestrictions>> {
|
-> std::vector<NestedEditFlagsLabels<ChatRestrictions>> {
|
||||||
|
@ -579,14 +586,6 @@ template <typename Flags>
|
||||||
ApplyDependencies(state->checkViews, dependencies, view);
|
ApplyDependencies(state->checkViews, dependencies, view);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (descriptor.header) {
|
|
||||||
container->add(
|
|
||||||
object_ptr<Ui::FlatLabel>(
|
|
||||||
container,
|
|
||||||
std::move(descriptor.header),
|
|
||||||
st::rightsHeaderLabel),
|
|
||||||
st::rightsHeaderMargin);
|
|
||||||
}
|
|
||||||
const auto addCheckbox = [&](
|
const auto addCheckbox = [&](
|
||||||
not_null<Ui::VerticalLayout*> verticalLayout,
|
not_null<Ui::VerticalLayout*> verticalLayout,
|
||||||
bool isInner,
|
bool isInner,
|
||||||
|
@ -1142,9 +1141,11 @@ void ShowEditPeerPermissionsBox(
|
||||||
return result;
|
return result;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
Ui::AddSubsectionTitle(
|
||||||
|
inner,
|
||||||
|
tr::lng_rights_default_restrictions_header());
|
||||||
auto [checkboxes, getRestrictions, changes] = CreateEditRestrictions(
|
auto [checkboxes, getRestrictions, changes] = CreateEditRestrictions(
|
||||||
inner,
|
inner,
|
||||||
tr::lng_rights_default_restrictions_header(),
|
|
||||||
restrictions,
|
restrictions,
|
||||||
disabledMessages,
|
disabledMessages,
|
||||||
{ .isForum = peer->isForum() });
|
{ .isForum = peer->isForum() });
|
||||||
|
@ -1308,7 +1309,6 @@ std::vector<AdminRightLabel> AdminRightLabels(
|
||||||
|
|
||||||
EditFlagsControl<ChatRestrictions> CreateEditRestrictions(
|
EditFlagsControl<ChatRestrictions> CreateEditRestrictions(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
rpl::producer<QString> header,
|
|
||||||
ChatRestrictions restrictions,
|
ChatRestrictions restrictions,
|
||||||
base::flat_map<ChatRestrictions, QString> disabledMessages,
|
base::flat_map<ChatRestrictions, QString> disabledMessages,
|
||||||
Data::RestrictionsSetOptions options) {
|
Data::RestrictionsSetOptions options) {
|
||||||
|
@ -1317,7 +1317,6 @@ EditFlagsControl<ChatRestrictions> CreateEditRestrictions(
|
||||||
widget.data(),
|
widget.data(),
|
||||||
NegateRestrictions(restrictions),
|
NegateRestrictions(restrictions),
|
||||||
{
|
{
|
||||||
.header = std::move(header),
|
|
||||||
.labels = NestedRestrictionLabelsList(options),
|
.labels = NestedRestrictionLabelsList(options),
|
||||||
.disabledMessages = std::move(disabledMessages),
|
.disabledMessages = std::move(disabledMessages),
|
||||||
});
|
});
|
||||||
|
@ -1334,7 +1333,6 @@ EditFlagsControl<ChatRestrictions> CreateEditRestrictions(
|
||||||
|
|
||||||
EditFlagsControl<ChatAdminRights> CreateEditAdminRights(
|
EditFlagsControl<ChatAdminRights> CreateEditAdminRights(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
rpl::producer<QString> header,
|
|
||||||
ChatAdminRights rights,
|
ChatAdminRights rights,
|
||||||
base::flat_map<ChatAdminRights, QString> disabledMessages,
|
base::flat_map<ChatAdminRights, QString> disabledMessages,
|
||||||
Data::AdminRightsSetOptions options) {
|
Data::AdminRightsSetOptions options) {
|
||||||
|
@ -1343,7 +1341,6 @@ EditFlagsControl<ChatAdminRights> CreateEditAdminRights(
|
||||||
widget.data(),
|
widget.data(),
|
||||||
rights,
|
rights,
|
||||||
{
|
{
|
||||||
.header = std::move(header),
|
|
||||||
.labels = NestedAdminRightLabels(options),
|
.labels = NestedAdminRightLabels(options),
|
||||||
.disabledMessages = std::move(disabledMessages),
|
.disabledMessages = std::move(disabledMessages),
|
||||||
});
|
});
|
||||||
|
@ -1434,3 +1431,18 @@ EditFlagsControl<PowerSaving::Flags> CreateEditPowerSaving(
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EditFlagsControl<AdminLog::FilterValue::Flags> CreateEditAdminLogFilter(
|
||||||
|
QWidget *parent,
|
||||||
|
AdminLog::FilterValue::Flags flags,
|
||||||
|
bool isChannel) {
|
||||||
|
auto widget = object_ptr<Ui::VerticalLayout>(parent);
|
||||||
|
auto descriptor = AdminLog::FilterValueLabels(isChannel);
|
||||||
|
auto result = CreateEditFlags(
|
||||||
|
widget.data(),
|
||||||
|
flags,
|
||||||
|
std::move(descriptor));
|
||||||
|
result.widget = std::move(widget);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -7,8 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "data/data_chat_participant_status.h"
|
|
||||||
#include "base/object_ptr.h"
|
#include "base/object_ptr.h"
|
||||||
|
#include "data/data_chat_participant_status.h"
|
||||||
|
#include "history/admin_log/history_admin_log_filter_value.h"
|
||||||
|
|
||||||
namespace style {
|
namespace style {
|
||||||
struct SettingsButton;
|
struct SettingsButton;
|
||||||
|
@ -72,7 +73,6 @@ struct NestedEditFlagsLabels {
|
||||||
|
|
||||||
template <typename Flags>
|
template <typename Flags>
|
||||||
struct EditFlagsDescriptor {
|
struct EditFlagsDescriptor {
|
||||||
rpl::producer<QString> header;
|
|
||||||
std::vector<NestedEditFlagsLabels<Flags>> labels;
|
std::vector<NestedEditFlagsLabels<Flags>> labels;
|
||||||
base::flat_map<Flags, QString> disabledMessages;
|
base::flat_map<Flags, QString> disabledMessages;
|
||||||
const style::SettingsButton *st = nullptr;
|
const style::SettingsButton *st = nullptr;
|
||||||
|
@ -89,7 +89,6 @@ using AdminRightLabel = EditFlagsLabel<ChatAdminRights>;
|
||||||
|
|
||||||
[[nodiscard]] auto CreateEditRestrictions(
|
[[nodiscard]] auto CreateEditRestrictions(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
rpl::producer<QString> header,
|
|
||||||
ChatRestrictions restrictions,
|
ChatRestrictions restrictions,
|
||||||
base::flat_map<ChatRestrictions, QString> disabledMessages,
|
base::flat_map<ChatRestrictions, QString> disabledMessages,
|
||||||
Data::RestrictionsSetOptions options)
|
Data::RestrictionsSetOptions options)
|
||||||
|
@ -97,7 +96,6 @@ using AdminRightLabel = EditFlagsLabel<ChatAdminRights>;
|
||||||
|
|
||||||
[[nodiscard]] auto CreateEditAdminRights(
|
[[nodiscard]] auto CreateEditAdminRights(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
rpl::producer<QString> header,
|
|
||||||
ChatAdminRights rights,
|
ChatAdminRights rights,
|
||||||
base::flat_map<ChatAdminRights, QString> disabledMessages,
|
base::flat_map<ChatAdminRights, QString> disabledMessages,
|
||||||
Data::AdminRightsSetOptions options)
|
Data::AdminRightsSetOptions options)
|
||||||
|
@ -115,3 +113,9 @@ using AdminRightLabel = EditFlagsLabel<ChatAdminRights>;
|
||||||
PowerSaving::Flags flags,
|
PowerSaving::Flags flags,
|
||||||
rpl::producer<QString> forceDisabledMessage
|
rpl::producer<QString> forceDisabledMessage
|
||||||
) -> EditFlagsControl<PowerSaving::Flags>;
|
) -> EditFlagsControl<PowerSaving::Flags>;
|
||||||
|
|
||||||
|
[[nodiscard]] auto CreateEditAdminLogFilter(
|
||||||
|
QWidget *parent,
|
||||||
|
AdminLog::FilterValue::Flags flags,
|
||||||
|
bool isChannel
|
||||||
|
) -> EditFlagsControl<AdminLog::FilterValue::Flags>;
|
||||||
|
|
|
@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/continuous_sliders.h"
|
#include "ui/widgets/continuous_sliders.h"
|
||||||
#include "ui/widgets/fields/input_field.h"
|
#include "ui/widgets/fields/input_field.h"
|
||||||
#include "ui/wrap/slide_wrap.h"
|
#include "ui/wrap/slide_wrap.h"
|
||||||
|
#include "ui/ui_utility.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "window/window_session_controller_link_info.h"
|
#include "window/window_session_controller_link_info.h"
|
||||||
#include "styles/style_chat_helpers.h"
|
#include "styles/style_chat_helpers.h"
|
||||||
|
|
|
@ -241,8 +241,8 @@ RequestsBoxController::RowHelper::RowHelper(bool isGroup)
|
||||||
? tr::lng_group_requests_add(tr::now)
|
? tr::lng_group_requests_add(tr::now)
|
||||||
: tr::lng_group_requests_add_channel(tr::now))
|
: tr::lng_group_requests_add_channel(tr::now))
|
||||||
, _rejectText(tr::lng_group_requests_dismiss(tr::now))
|
, _rejectText(tr::lng_group_requests_dismiss(tr::now))
|
||||||
, _acceptTextWidth(st::requestsAcceptButton.font->width(_acceptText))
|
, _acceptTextWidth(st::requestsAcceptButton.style.font->width(_acceptText))
|
||||||
, _rejectTextWidth(st::requestsRejectButton.font->width(_rejectText)) {
|
, _rejectTextWidth(st::requestsRejectButton.style.font->width(_rejectText)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestsBoxController::RequestsBoxController(
|
RequestsBoxController::RequestsBoxController(
|
||||||
|
@ -491,7 +491,7 @@ void RequestsBoxController::RowHelper::paintButton(
|
||||||
const auto textLeft = geometry.x()
|
const auto textLeft = geometry.x()
|
||||||
+ ((geometry.width() - textWidth) / 2);
|
+ ((geometry.width() - textWidth) / 2);
|
||||||
const auto textTop = geometry.y() + st.textTop;
|
const auto textTop = geometry.y() + st.textTop;
|
||||||
p.setFont(st.font);
|
p.setFont(st.style.font);
|
||||||
p.setPen(over ? st.textFgOver : st.textFg);
|
p.setPen(over ? st.textFgOver : st.textFg);
|
||||||
p.drawTextLeft(textLeft, textTop, outerWidth, text);
|
p.drawTextLeft(textLeft, textTop, outerWidth, text);
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/wrap/slide_wrap.h"
|
#include "ui/wrap/slide_wrap.h"
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
#include "ui/widgets/fields/special_fields.h"
|
#include "ui/widgets/fields/special_fields.h"
|
||||||
|
#include "ui/ui_utility.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "settings/settings_common.h"
|
#include "settings/settings_common.h"
|
||||||
#include "styles/style_layers.h"
|
#include "styles/style_layers.h"
|
||||||
|
|
|
@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "ui/wrap/vertical_layout_reorder.h"
|
#include "ui/wrap/vertical_layout_reorder.h"
|
||||||
|
#include "ui/ui_utility.h"
|
||||||
#include "styles/style_boxes.h" // contactsStatusFont.
|
#include "styles/style_boxes.h" // contactsStatusFont.
|
||||||
#include "styles/style_info.h"
|
#include "styles/style_info.h"
|
||||||
#include "styles/style_layers.h"
|
#include "styles/style_layers.h"
|
||||||
|
|
|
@ -721,6 +721,7 @@ void PeerShortInfoBox::prepare() {
|
||||||
_roundedTop.setDevicePixelRatio(style::DevicePixelRatio());
|
_roundedTop.setDevicePixelRatio(style::DevicePixelRatio());
|
||||||
refreshRoundedTopImage(getDelegate()->style().bg->c);
|
refreshRoundedTopImage(getDelegate()->style().bg->c);
|
||||||
|
|
||||||
|
setCustomCornersFilling(RectPart::FullTop);
|
||||||
setDimensionsToContent(st::shortInfoWidth, _rows);
|
setDimensionsToContent(st::shortInfoWidth, _rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -795,10 +796,6 @@ void PeerShortInfoBox::prepareRows() {
|
||||||
tr::lng_mediaview_copy(tr::now));
|
tr::lng_mediaview_copy(tr::now));
|
||||||
}
|
}
|
||||||
|
|
||||||
RectParts PeerShortInfoBox::customCornersFilling() {
|
|
||||||
return RectPart::FullTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PeerShortInfoBox::resizeEvent(QResizeEvent *e) {
|
void PeerShortInfoBox::resizeEvent(QResizeEvent *e) {
|
||||||
BoxContent::resizeEvent(e);
|
BoxContent::resizeEvent(e);
|
||||||
|
|
||||||
|
|
|
@ -162,7 +162,6 @@ public:
|
||||||
private:
|
private:
|
||||||
void prepare() override;
|
void prepare() override;
|
||||||
void prepareRows();
|
void prepareRows();
|
||||||
RectParts customCornersFilling() override;
|
|
||||||
|
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,8 @@ void ProcessUserpic(
|
||||||
if (!state->userpicView.cloud) {
|
if (!state->userpicView.cloud) {
|
||||||
GenerateImage(
|
GenerateImage(
|
||||||
state,
|
state,
|
||||||
peer->generateUserpicImage(
|
PeerData::GenerateUserpicImage(
|
||||||
|
peer,
|
||||||
state->userpicView,
|
state->userpicView,
|
||||||
st::shortInfoWidth * style::DevicePixelRatio(),
|
st::shortInfoWidth * style::DevicePixelRatio(),
|
||||||
0),
|
0),
|
||||||
|
|
|
@ -23,7 +23,7 @@ using Type = SelfDestructionBox::Type;
|
||||||
|
|
||||||
[[nodiscard]] std::vector<int> Values(Type type) {
|
[[nodiscard]] std::vector<int> Values(Type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Type::Account: return { 30, 90, 180, 365 };
|
case Type::Account: return { 30, 90, 180, 365, 548, 720 };
|
||||||
case Type::Sessions: return { 7, 30, 90, 180, 365 };
|
case Type::Sessions: return { 7, 30, 90, 180, 365 };
|
||||||
}
|
}
|
||||||
Unexpected("SelfDestructionBox::Type in Values.");
|
Unexpected("SelfDestructionBox::Type in Values.");
|
||||||
|
@ -113,8 +113,8 @@ void SelfDestructionBox::showContent() {
|
||||||
QString SelfDestructionBox::DaysLabel(int days) {
|
QString SelfDestructionBox::DaysLabel(int days) {
|
||||||
return !days
|
return !days
|
||||||
? QString()
|
? QString()
|
||||||
: (days > 364)
|
//: (days > 364)
|
||||||
? tr::lng_years(tr::now, lt_count, days / 365)
|
//? tr::lng_years(tr::now, lt_count, days / 365)
|
||||||
: (days > 25)
|
: (days > 25)
|
||||||
? tr::lng_months(tr::now, lt_count, std::max(days / 30, 1))
|
? tr::lng_months(tr::now, lt_count, std::max(days / 30, 1))
|
||||||
: tr::lng_weeks(tr::now, lt_count, std::max(days / 7, 1));
|
: tr::lng_weeks(tr::now, lt_count, std::max(days / 7, 1));
|
||||||
|
|
|
@ -46,6 +46,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/controls/emoji_button.h"
|
#include "ui/controls/emoji_button.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/vertical_list.h"
|
#include "ui/vertical_list.h"
|
||||||
|
#include "ui/ui_utility.h"
|
||||||
#include "lottie/lottie_single_player.h"
|
#include "lottie/lottie_single_player.h"
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
|
|
276
Telegram/SourceFiles/boxes/send_gif_with_caption_box.cpp
Normal file
|
@ -0,0 +1,276 @@
|
||||||
|
/*
|
||||||
|
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 "boxes/send_gif_with_caption_box.h"
|
||||||
|
|
||||||
|
#include "boxes/premium_preview_box.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 "data/data_document.h"
|
||||||
|
#include "data/data_document_media.h"
|
||||||
|
#include "data/data_file_origin.h"
|
||||||
|
#include "data/data_peer_values.h"
|
||||||
|
#include "data/data_premium_limits.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_user.h"
|
||||||
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
|
#include "data/stickers/data_stickers.h"
|
||||||
|
#include "history/view/controls/history_view_characters_limit.h"
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
#include "media/clip/media_clip_reader.h"
|
||||||
|
#include "menu/menu_send.h"
|
||||||
|
#include "ui/controls/emoji_button.h"
|
||||||
|
#include "ui/controls/emoji_button_factory.h"
|
||||||
|
#include "ui/layers/generic_box.h"
|
||||||
|
#include "ui/rect.h"
|
||||||
|
#include "ui/vertical_list.h"
|
||||||
|
#include "ui/widgets/fields/input_field.h"
|
||||||
|
#include "window/window_controller.h"
|
||||||
|
#include "window/window_session_controller.h"
|
||||||
|
#include "styles/style_boxes.h"
|
||||||
|
#include "styles/style_chat_helpers.h"
|
||||||
|
#include "styles/style_layers.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
[[nodiscard]] not_null<Ui::RpWidget*> AddGifWidget(
|
||||||
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
not_null<DocumentData*> document,
|
||||||
|
int width) {
|
||||||
|
struct State final {
|
||||||
|
std::shared_ptr<Data::DocumentMedia> mediaView;
|
||||||
|
::Media::Clip::ReaderPointer gif;
|
||||||
|
rpl::lifetime loadingLifetime;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto state = container->lifetime().make_state<State>();
|
||||||
|
state->mediaView = document->createMediaView();
|
||||||
|
state->mediaView->automaticLoad(Data::FileOriginSavedGifs(), nullptr);
|
||||||
|
state->mediaView->thumbnailWanted(Data::FileOriginSavedGifs());
|
||||||
|
state->mediaView->videoThumbnailWanted(Data::FileOriginSavedGifs());
|
||||||
|
|
||||||
|
const auto widget = container->add(
|
||||||
|
Ui::CreateSkipWidget(
|
||||||
|
container,
|
||||||
|
document->dimensions.scaled(
|
||||||
|
width - rect::m::sum::h(st::boxRowPadding),
|
||||||
|
std::numeric_limits<int>::max(),
|
||||||
|
Qt::KeepAspectRatio).height()),
|
||||||
|
st::boxRowPadding);
|
||||||
|
widget->paintRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
auto p = QPainter(widget);
|
||||||
|
if (state->gif && state->gif->started()) {
|
||||||
|
p.drawImage(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
state->gif->current({ .frame = widget->size() }, crl::now()));
|
||||||
|
} else if (const auto thumb = state->mediaView->thumbnail()) {
|
||||||
|
p.drawImage(
|
||||||
|
widget->rect(),
|
||||||
|
thumb->pixNoCache(
|
||||||
|
widget->size() * style::DevicePixelRatio(),
|
||||||
|
{ .outer = widget->size() }).toImage());
|
||||||
|
} else if (const auto thumb = state->mediaView->thumbnailInline()) {
|
||||||
|
p.drawImage(
|
||||||
|
widget->rect(),
|
||||||
|
thumb->pixNoCache(
|
||||||
|
widget->size() * style::DevicePixelRatio(),
|
||||||
|
{
|
||||||
|
.options = Images::Option::Blur,
|
||||||
|
.outer = widget->size(),
|
||||||
|
}).toImage());
|
||||||
|
}
|
||||||
|
}, widget->lifetime());
|
||||||
|
|
||||||
|
const auto updateThumbnail = [=] {
|
||||||
|
if (document->dimensions.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!state->mediaView->loaded()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto callback = [=](::Media::Clip::Notification) {
|
||||||
|
if (state->gif && state->gif->ready() && !state->gif->started()) {
|
||||||
|
state->gif->start({ .frame = widget->size() });
|
||||||
|
}
|
||||||
|
widget->update();
|
||||||
|
};
|
||||||
|
state->gif = ::Media::Clip::MakeReader(
|
||||||
|
state->mediaView->owner()->location(),
|
||||||
|
state->mediaView->bytes(),
|
||||||
|
callback);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
if (!updateThumbnail()) {
|
||||||
|
document->owner().session().downloaderTaskFinished(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
if (updateThumbnail()) {
|
||||||
|
state->loadingLifetime.destroy();
|
||||||
|
widget->update();
|
||||||
|
}
|
||||||
|
}, state->loadingLifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] not_null<Ui::InputField*> AddInputField(
|
||||||
|
not_null<Ui::GenericBox*> box,
|
||||||
|
not_null<Window::SessionController*> controller) {
|
||||||
|
using Limit = HistoryView::Controls::CharactersLimitLabel;
|
||||||
|
|
||||||
|
const auto bottomContainer = box->setPinnedToBottomContent(
|
||||||
|
object_ptr<Ui::VerticalLayout>(box));
|
||||||
|
const auto wrap = bottomContainer->add(
|
||||||
|
object_ptr<Ui::RpWidget>(box),
|
||||||
|
st::boxRowPadding);
|
||||||
|
const auto input = Ui::CreateChild<Ui::InputField>(
|
||||||
|
wrap,
|
||||||
|
st::defaultComposeFiles.caption,
|
||||||
|
Ui::InputField::Mode::MultiLine,
|
||||||
|
tr::lng_photo_caption());
|
||||||
|
Ui::ResizeFitChild(wrap, input);
|
||||||
|
|
||||||
|
struct State final {
|
||||||
|
base::unique_qptr<ChatHelpers::TabbedPanel> emojiPanel;
|
||||||
|
base::unique_qptr<Limit> charsLimitation;
|
||||||
|
};
|
||||||
|
const auto state = box->lifetime().make_state<State>();
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto container = box->getDelegate()->outerContainer();
|
||||||
|
using Selector = ChatHelpers::TabbedSelector;
|
||||||
|
state->emojiPanel = base::make_unique_q<ChatHelpers::TabbedPanel>(
|
||||||
|
container,
|
||||||
|
controller,
|
||||||
|
object_ptr<Selector>(
|
||||||
|
nullptr,
|
||||||
|
controller->uiShow(),
|
||||||
|
Window::GifPauseReason::Layer,
|
||||||
|
Selector::Mode::EmojiOnly));
|
||||||
|
const auto emojiPanel = state->emojiPanel.get();
|
||||||
|
emojiPanel->setDesiredHeightValues(
|
||||||
|
1.,
|
||||||
|
st::emojiPanMinHeight / 2,
|
||||||
|
st::emojiPanMinHeight);
|
||||||
|
emojiPanel->hide();
|
||||||
|
emojiPanel->selector()->setCurrentPeer(controller->session().user());
|
||||||
|
emojiPanel->selector()->emojiChosen(
|
||||||
|
) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) {
|
||||||
|
Ui::InsertEmojiAtCursor(input->textCursor(), data.emoji);
|
||||||
|
}, input->lifetime());
|
||||||
|
emojiPanel->selector()->customEmojiChosen(
|
||||||
|
) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
|
||||||
|
const auto info = data.document->sticker();
|
||||||
|
if (info
|
||||||
|
&& info->setType == Data::StickersType::Emoji
|
||||||
|
&& !controller->session().premium()) {
|
||||||
|
ShowPremiumPreviewBox(
|
||||||
|
controller,
|
||||||
|
PremiumFeature::AnimatedEmoji);
|
||||||
|
} else {
|
||||||
|
Data::InsertCustomEmoji(input, data.document);
|
||||||
|
}
|
||||||
|
}, input->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto emojiButton = Ui::AddEmojiToggleToField(
|
||||||
|
input,
|
||||||
|
box,
|
||||||
|
controller,
|
||||||
|
state->emojiPanel.get(),
|
||||||
|
st::sendGifWithCaptionEmojiPosition);
|
||||||
|
emojiButton->show();
|
||||||
|
|
||||||
|
const auto session = &controller->session();
|
||||||
|
const auto checkCharsLimitation = [=](auto repeat) -> void {
|
||||||
|
const auto remove = Ui::ComputeFieldCharacterCount(input)
|
||||||
|
- Data::PremiumLimits(session).captionLengthCurrent();
|
||||||
|
if (remove > 0) {
|
||||||
|
if (!state->charsLimitation) {
|
||||||
|
state->charsLimitation = base::make_unique_q<Limit>(
|
||||||
|
input,
|
||||||
|
emojiButton,
|
||||||
|
style::al_top);
|
||||||
|
state->charsLimitation->show();
|
||||||
|
Data::AmPremiumValue(session) | rpl::start_with_next([=] {
|
||||||
|
repeat(repeat);
|
||||||
|
}, state->charsLimitation->lifetime());
|
||||||
|
}
|
||||||
|
state->charsLimitation->setLeft(remove);
|
||||||
|
state->charsLimitation->show();
|
||||||
|
} else {
|
||||||
|
state->charsLimitation = nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
input->changes() | rpl::start_with_next([=] {
|
||||||
|
checkCharsLimitation(checkCharsLimitation);
|
||||||
|
}, input->lifetime());
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void SendGifWithCaptionBox(
|
||||||
|
not_null<Ui::GenericBox*> box,
|
||||||
|
not_null<DocumentData*> document,
|
||||||
|
const SendMenu::Details &details,
|
||||||
|
Fn<void(Api::SendOptions, TextWithTags)> done) {
|
||||||
|
const auto window = Core::App().findWindow(box);
|
||||||
|
const auto controller = window ? window->sessionController() : nullptr;
|
||||||
|
if (!controller) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
box->setTitle(tr::lng_send_gif_with_caption());
|
||||||
|
box->setWidth(st::boxWidth);
|
||||||
|
box->getDelegate()->setStyle(st::sendGifBox);
|
||||||
|
|
||||||
|
const auto container = box->verticalLayout();
|
||||||
|
[[maybe_unused]] const auto gifWidget = AddGifWidget(
|
||||||
|
container,
|
||||||
|
document,
|
||||||
|
st::boxWidth);
|
||||||
|
|
||||||
|
Ui::AddSkip(container);
|
||||||
|
|
||||||
|
const auto input = AddInputField(box, controller);
|
||||||
|
box->setFocusCallback([=] {
|
||||||
|
input->setFocus();
|
||||||
|
});
|
||||||
|
|
||||||
|
input->setSubmitSettings(Core::App().settings().sendSubmitWay());
|
||||||
|
InitMessageField(controller, input, [=](not_null<DocumentData*>) {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto send = [=](Api::SendOptions options) {
|
||||||
|
done(std::move(options), input->getTextWithTags());
|
||||||
|
};
|
||||||
|
const auto confirm = box->addButton(
|
||||||
|
tr::lng_send_button(),
|
||||||
|
[=] { send({}); });
|
||||||
|
SendMenu::SetupMenuAndShortcuts(
|
||||||
|
confirm,
|
||||||
|
controller->uiShow(),
|
||||||
|
[=] { return details; },
|
||||||
|
SendMenu::DefaultCallback(controller->uiShow(), send));
|
||||||
|
box->addButton(tr::lng_cancel(), [=] {
|
||||||
|
box->closeBox();
|
||||||
|
});
|
||||||
|
input->submits(
|
||||||
|
) | rpl::start_with_next([=] { send({}); }, input->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Ui
|
30
Telegram/SourceFiles/boxes/send_gif_with_caption_box.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class DocumentData;
|
||||||
|
|
||||||
|
namespace Api {
|
||||||
|
struct SendOptions;
|
||||||
|
} // namespace Api
|
||||||
|
|
||||||
|
namespace SendMenu {
|
||||||
|
struct Details;
|
||||||
|
} // namespace SendMenu
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
|
||||||
|
class GenericBox;
|
||||||
|
|
||||||
|
void SendGifWithCaptionBox(
|
||||||
|
not_null<Ui::GenericBox*> box,
|
||||||
|
not_null<DocumentData*> document,
|
||||||
|
const SendMenu::Details &details,
|
||||||
|
Fn<void(Api::SendOptions, TextWithTags)> done);
|
||||||
|
|
||||||
|
} // namespace Ui
|
|
@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/text/text_options.h"
|
#include "ui/text/text_options.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
|
#include "ui/ui_utility.h"
|
||||||
#include "chat_helpers/message_field.h"
|
#include "chat_helpers/message_field.h"
|
||||||
#include "menu/menu_check_item.h"
|
#include "menu/menu_check_item.h"
|
||||||
#include "menu/menu_send.h"
|
#include "menu/menu_send.h"
|
||||||
|
|
|
@ -1216,11 +1216,12 @@ StickersBox::Inner::Inner(
|
||||||
})
|
})
|
||||||
, _itemsTop(st::lineWidth)
|
, _itemsTop(st::lineWidth)
|
||||||
, _addText(tr::lng_stickers_featured_add(tr::now))
|
, _addText(tr::lng_stickers_featured_add(tr::now))
|
||||||
, _addWidth(st::stickersTrendingAdd.font->width(_addText))
|
, _addWidth(st::stickersTrendingAdd.style.font->width(_addText))
|
||||||
, _undoText(tr::lng_stickers_return(tr::now))
|
, _undoText(tr::lng_stickers_return(tr::now))
|
||||||
, _undoWidth(st::stickersUndoRemove.font->width(_undoText))
|
, _undoWidth(st::stickersUndoRemove.style.font->width(_undoText))
|
||||||
, _installedText(tr::lng_stickers_featured_installed(tr::now))
|
, _installedText(tr::lng_stickers_featured_installed(tr::now))
|
||||||
, _installedWidth(st::stickersTrendingInstalled.font->width(_installedText)) {
|
, _installedWidth(st::stickersTrendingInstalled.style.font->width(
|
||||||
|
_installedText)) {
|
||||||
setup();
|
setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1666,7 +1667,7 @@ void StickersBox::Inner::paintFakeButton(Painter &p, not_null<Row*> row, int ind
|
||||||
row->ripple.reset();
|
row->ripple.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.setFont(st.font);
|
p.setFont(st.style.font);
|
||||||
p.setPen(st.textFg);
|
p.setPen(st.textFg);
|
||||||
p.drawTextLeft(rect.x() - (st.width / 2), rect.y() + st.textTop, width(), text, textWidth);
|
p.drawTextLeft(rect.x() - (st.width / 2), rect.y() + st.textTop, width(), text, textWidth);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1700,7 +1701,7 @@ void StickersBox::Inner::paintFakeButton(Painter &p, not_null<Row*> row, int ind
|
||||||
row->ripple.reset();
|
row->ripple.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.setFont(st.font);
|
p.setFont(st.style.font);
|
||||||
p.setPen(selected ? st.textFgOver : st.textFg);
|
p.setPen(selected ? st.textFgOver : st.textFg);
|
||||||
p.drawTextLeft(rect.x() - (st.width / 2), rect.y() + st.textTop, width(), text, textWidth);
|
p.drawTextLeft(rect.x() - (st.width / 2), rect.y() + st.textTop, width(), text, textWidth);
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,6 +155,8 @@ callMicrophoneMute: CallButton(callAnswer) {
|
||||||
bg: callIconBg;
|
bg: callIconBg;
|
||||||
outerBg: callMuteRipple;
|
outerBg: callMuteRipple;
|
||||||
label: callButtonLabel;
|
label: callButtonLabel;
|
||||||
|
cornerButtonPosition: point(40px, 4px);
|
||||||
|
cornerButtonBorder: 2px;
|
||||||
}
|
}
|
||||||
callMicrophoneUnmute: CallButton(callMicrophoneMute) {
|
callMicrophoneUnmute: CallButton(callMicrophoneMute) {
|
||||||
button: IconButton(callButton) {
|
button: IconButton(callButton) {
|
||||||
|
@ -181,6 +183,34 @@ callCameraUnmute: CallButton(callMicrophoneUnmute) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
callCornerButtonInner: IconButton {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
|
||||||
|
iconPosition: point(-1px, -1px);
|
||||||
|
|
||||||
|
rippleAreaPosition: point(0px, 0px);
|
||||||
|
rippleAreaSize: 20px;
|
||||||
|
ripple: defaultRippleAnimation;
|
||||||
|
}
|
||||||
|
callCornerButton: CallButton(callMicrophoneMute) {
|
||||||
|
button: IconButton(callCornerButtonInner) {
|
||||||
|
icon: icon {{ "calls/mini_calls_arrow", callIconFg }};
|
||||||
|
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||||
|
color: callMuteRipple;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bgSize: 20px;
|
||||||
|
bgPosition: point(0px, 0px);
|
||||||
|
}
|
||||||
|
callCornerButtonInactive: CallButton(callMicrophoneUnmute, callCornerButton) {
|
||||||
|
button: IconButton(callCornerButtonInner) {
|
||||||
|
icon: icon {{ "calls/mini_calls_arrow", callIconFgActive }};
|
||||||
|
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||||
|
color: callIconActiveRipple;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
callScreencastOn: CallButton(callMicrophoneMute) {
|
callScreencastOn: CallButton(callMicrophoneMute) {
|
||||||
button: IconButton(callButton) {
|
button: IconButton(callButton) {
|
||||||
icon: icon {{ "calls/calls_present", callIconFg }};
|
icon: icon {{ "calls/calls_present", callIconFg }};
|
||||||
|
@ -576,6 +606,18 @@ groupCallMenuAbout: FlatLabel(defaultFlatLabel) {
|
||||||
minWidth: 200px;
|
minWidth: 200px;
|
||||||
maxHeight: 92px;
|
maxHeight: 92px;
|
||||||
}
|
}
|
||||||
|
callDeviceSelectionLabel: FlatLabel(defaultSubsectionTitle) {
|
||||||
|
textFg: groupCallActiveFg;
|
||||||
|
minWidth: 200px;
|
||||||
|
maxHeight: 20px;
|
||||||
|
}
|
||||||
|
callDeviceSelectionMenu: PopupMenu(groupCallPopupMenu) {
|
||||||
|
scrollPadding: margins(0px, 3px, 0px, 8px);
|
||||||
|
menu: Menu(groupCallMenu) {
|
||||||
|
widthMin: 240px;
|
||||||
|
itemPadding: margins(17px, 8px, 17px, 7px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
groupCallRecordingTimerPadding: margins(0px, 4px, 0px, 4px);
|
groupCallRecordingTimerPadding: margins(0px, 4px, 0px, 4px);
|
||||||
groupCallRecordingTimerFont: font(12px);
|
groupCallRecordingTimerFont: font(12px);
|
||||||
|
|
|
@ -1310,6 +1310,19 @@ void Call::toggleScreenSharing(std::optional<QString> uniqueId) {
|
||||||
_videoOutgoing->setState(Webrtc::VideoState::Active);
|
_videoOutgoing->setState(Webrtc::VideoState::Active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Call::playbackDeviceIdValue() const
|
||||||
|
-> rpl::producer<Webrtc::DeviceResolvedId> {
|
||||||
|
return _playbackDeviceId.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<Webrtc::DeviceResolvedId> Call::captureDeviceIdValue() const {
|
||||||
|
return _captureDeviceId.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<Webrtc::DeviceResolvedId> Call::cameraDeviceIdValue() const {
|
||||||
|
return _cameraDeviceId.value();
|
||||||
|
}
|
||||||
|
|
||||||
void Call::finish(FinishType type, const MTPPhoneCallDiscardReason &reason) {
|
void Call::finish(FinishType type, const MTPPhoneCallDiscardReason &reason) {
|
||||||
Expects(type != FinishType::None);
|
Expects(type != FinishType::None);
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ enum class AudioState;
|
||||||
namespace Webrtc {
|
namespace Webrtc {
|
||||||
enum class VideoState;
|
enum class VideoState;
|
||||||
class VideoTrack;
|
class VideoTrack;
|
||||||
|
struct DeviceResolvedId;
|
||||||
} // namespace Webrtc
|
} // namespace Webrtc
|
||||||
|
|
||||||
namespace Calls {
|
namespace Calls {
|
||||||
|
@ -220,6 +221,13 @@ public:
|
||||||
void toggleCameraSharing(bool enabled);
|
void toggleCameraSharing(bool enabled);
|
||||||
void toggleScreenSharing(std::optional<QString> uniqueId);
|
void toggleScreenSharing(std::optional<QString> uniqueId);
|
||||||
|
|
||||||
|
[[nodiscard]] auto playbackDeviceIdValue() const
|
||||||
|
-> rpl::producer<Webrtc::DeviceResolvedId>;
|
||||||
|
[[nodiscard]] auto captureDeviceIdValue() const
|
||||||
|
-> rpl::producer<Webrtc::DeviceResolvedId>;
|
||||||
|
[[nodiscard]] auto cameraDeviceIdValue() const
|
||||||
|
-> rpl::producer<Webrtc::DeviceResolvedId>;
|
||||||
|
|
||||||
[[nodiscard]] rpl::lifetime &lifetime() {
|
[[nodiscard]] rpl::lifetime &lifetime() {
|
||||||
return _lifetime;
|
return _lifetime;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_cloud_file.h"
|
#include "data/data_cloud_file.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
#include "calls/group/calls_group_common.h"
|
#include "calls/group/calls_group_common.h"
|
||||||
|
#include "calls/ui/calls_device_menu.h"
|
||||||
#include "calls/calls_emoji_fingerprint.h"
|
#include "calls/calls_emoji_fingerprint.h"
|
||||||
#include "calls/calls_signal_bars.h"
|
#include "calls/calls_signal_bars.h"
|
||||||
#include "calls/calls_userpic.h"
|
#include "calls/calls_userpic.h"
|
||||||
|
@ -24,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/call_button.h"
|
#include "ui/widgets/call_button.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "ui/widgets/shadow.h"
|
#include "ui/widgets/shadow.h"
|
||||||
#include "ui/widgets/rp_window.h"
|
#include "ui/widgets/rp_window.h"
|
||||||
#include "ui/layers/layer_manager.h"
|
#include "ui/layers/layer_manager.h"
|
||||||
|
@ -130,6 +132,7 @@ Panel::Panel(not_null<Call*> call)
|
||||||
initWidget();
|
initWidget();
|
||||||
initControls();
|
initControls();
|
||||||
initLayout();
|
initLayout();
|
||||||
|
initMediaDeviceToggles();
|
||||||
showAndActivate();
|
showAndActivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -736,6 +739,58 @@ void Panel::initGeometry() {
|
||||||
updateControlsGeometry();
|
updateControlsGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Panel::initMediaDeviceToggles() {
|
||||||
|
_cameraDeviceToggle = _camera->addCornerButton(
|
||||||
|
st::callCornerButton,
|
||||||
|
&st::callCornerButtonInactive);
|
||||||
|
_audioDeviceToggle = _mute->entity()->addCornerButton(
|
||||||
|
st::callCornerButton,
|
||||||
|
&st::callCornerButtonInactive);
|
||||||
|
|
||||||
|
_cameraDeviceToggle->setClickedCallback([=] {
|
||||||
|
showDevicesMenu(_cameraDeviceToggle, {
|
||||||
|
{ Webrtc::DeviceType::Camera, _call->cameraDeviceIdValue() },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
_audioDeviceToggle->setClickedCallback([=] {
|
||||||
|
showDevicesMenu(_audioDeviceToggle, {
|
||||||
|
{ Webrtc::DeviceType::Playback, _call->playbackDeviceIdValue() },
|
||||||
|
{ Webrtc::DeviceType::Capture, _call->captureDeviceIdValue() },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel::showDevicesMenu(
|
||||||
|
not_null<QWidget*> button,
|
||||||
|
std::vector<DeviceSelection> types) {
|
||||||
|
if (!_call || _devicesMenu) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto chosen = [=](Webrtc::DeviceType type, QString id) {
|
||||||
|
switch (type) {
|
||||||
|
case Webrtc::DeviceType::Playback:
|
||||||
|
Core::App().settings().setCallPlaybackDeviceId(id);
|
||||||
|
break;
|
||||||
|
case Webrtc::DeviceType::Capture:
|
||||||
|
Core::App().settings().setCallCaptureDeviceId(id);
|
||||||
|
break;
|
||||||
|
case Webrtc::DeviceType::Camera:
|
||||||
|
Core::App().settings().setCameraDeviceId(id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Core::App().saveSettingsDelayed();
|
||||||
|
};
|
||||||
|
_devicesMenu = MakeDeviceSelectionMenu(
|
||||||
|
widget(),
|
||||||
|
&Core::App().mediaDevices(),
|
||||||
|
std::move(types),
|
||||||
|
chosen);
|
||||||
|
_devicesMenu->setForcedVerticalOrigin(
|
||||||
|
Ui::PopupMenu::VerticalOrigin::Bottom);
|
||||||
|
_devicesMenu->popup(button->mapToGlobal(QPoint())
|
||||||
|
- QPoint(st::callDeviceSelectionMenu.menu.widthMin / 2, 0));
|
||||||
|
}
|
||||||
|
|
||||||
void Panel::refreshOutgoingPreviewInBody(State state) {
|
void Panel::refreshOutgoingPreviewInBody(State state) {
|
||||||
const auto inBody = (state != State::Established)
|
const auto inBody = (state != State::Established)
|
||||||
&& (_call->videoOutgoing()->state() != Webrtc::VideoState::Inactive)
|
&& (_call->videoOutgoing()->state() != Webrtc::VideoState::Inactive)
|
||||||
|
|
|
@ -37,6 +37,7 @@ class FadeWrap;
|
||||||
template <typename Widget>
|
template <typename Widget>
|
||||||
class PaddingWrap;
|
class PaddingWrap;
|
||||||
class RpWindow;
|
class RpWindow;
|
||||||
|
class PopupMenu;
|
||||||
namespace GL {
|
namespace GL {
|
||||||
enum class Backend;
|
enum class Backend;
|
||||||
} // namespace GL
|
} // namespace GL
|
||||||
|
@ -55,6 +56,7 @@ namespace Calls {
|
||||||
class Userpic;
|
class Userpic;
|
||||||
class SignalBars;
|
class SignalBars;
|
||||||
class VideoBubble;
|
class VideoBubble;
|
||||||
|
struct DeviceSelection;
|
||||||
|
|
||||||
class Panel final : private Group::Ui::DesktopCapture::ChooseSourceDelegate {
|
class Panel final : private Group::Ui::DesktopCapture::ChooseSourceDelegate {
|
||||||
public:
|
public:
|
||||||
|
@ -104,6 +106,7 @@ private:
|
||||||
void initControls();
|
void initControls();
|
||||||
void reinitWithCall(Call *call);
|
void reinitWithCall(Call *call);
|
||||||
void initLayout();
|
void initLayout();
|
||||||
|
void initMediaDeviceToggles();
|
||||||
void initGeometry();
|
void initGeometry();
|
||||||
|
|
||||||
[[nodiscard]] bool handleClose() const;
|
[[nodiscard]] bool handleClose() const;
|
||||||
|
@ -126,6 +129,10 @@ private:
|
||||||
void showRemoteLowBattery();
|
void showRemoteLowBattery();
|
||||||
void refreshAnswerHangupRedialLabel();
|
void refreshAnswerHangupRedialLabel();
|
||||||
|
|
||||||
|
void showDevicesMenu(
|
||||||
|
not_null<QWidget*> button,
|
||||||
|
std::vector<DeviceSelection> types);
|
||||||
|
|
||||||
[[nodiscard]] QRect incomingFrameGeometry() const;
|
[[nodiscard]] QRect incomingFrameGeometry() const;
|
||||||
[[nodiscard]] QRect outgoingFrameGeometry() const;
|
[[nodiscard]] QRect outgoingFrameGeometry() const;
|
||||||
|
|
||||||
|
@ -156,8 +163,10 @@ private:
|
||||||
Ui::Animations::Simple _hangupShownProgress;
|
Ui::Animations::Simple _hangupShownProgress;
|
||||||
object_ptr<Ui::FadeWrap<Ui::CallButton>> _screencast;
|
object_ptr<Ui::FadeWrap<Ui::CallButton>> _screencast;
|
||||||
object_ptr<Ui::CallButton> _camera;
|
object_ptr<Ui::CallButton> _camera;
|
||||||
|
Ui::CallButton *_cameraDeviceToggle = nullptr;
|
||||||
base::unique_qptr<Ui::CallButton> _startVideo;
|
base::unique_qptr<Ui::CallButton> _startVideo;
|
||||||
object_ptr<Ui::FadeWrap<Ui::CallButton>> _mute;
|
object_ptr<Ui::FadeWrap<Ui::CallButton>> _mute;
|
||||||
|
Ui::CallButton *_audioDeviceToggle = nullptr;
|
||||||
object_ptr<Ui::FlatLabel> _name;
|
object_ptr<Ui::FlatLabel> _name;
|
||||||
object_ptr<Ui::FlatLabel> _status;
|
object_ptr<Ui::FlatLabel> _status;
|
||||||
object_ptr<Ui::RpWidget> _fingerprint = { nullptr };
|
object_ptr<Ui::RpWidget> _fingerprint = { nullptr };
|
||||||
|
@ -170,6 +179,8 @@ private:
|
||||||
int _bodyTop = 0;
|
int _bodyTop = 0;
|
||||||
int _buttonsTop = 0;
|
int _buttonsTop = 0;
|
||||||
|
|
||||||
|
base::unique_qptr<Ui::PopupMenu> _devicesMenu;
|
||||||
|
|
||||||
base::Timer _updateDurationTimer;
|
base::Timer _updateDurationTimer;
|
||||||
base::Timer _updateOuterRippleTimer;
|
base::Timer _updateOuterRippleTimer;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui/rect_part.h"
|
||||||
#include "ui/rp_widget.h"
|
#include "ui/rp_widget.h"
|
||||||
|
|
||||||
namespace Webrtc {
|
namespace Webrtc {
|
||||||
|
|
|
@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "lang/lang_hardcoded.h"
|
#include "lang/lang_hardcoded.h"
|
||||||
#include "boxes/peers/edit_participants_box.h" // SubscribeToMigration.
|
#include "boxes/peers/edit_participants_box.h" // SubscribeToMigration.
|
||||||
#include "ui/toast/toast.h"
|
#include "ui/toast/toast.h"
|
||||||
|
#include "ui/ui_utility.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "core/core_settings.h"
|
#include "core/core_settings.h"
|
||||||
|
|
|
@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/toast/toast.h"
|
#include "ui/toast/toast.h"
|
||||||
#include "ui/image/image_prepare.h"
|
#include "ui/image/image_prepare.h"
|
||||||
|
#include "ui/integration.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/round_rect.h"
|
#include "ui/round_rect.h"
|
||||||
#include "info/profile/info_profile_values.h" // Info::Profile::Value.
|
#include "info/profile/info_profile_values.h" // Info::Profile::Value.
|
||||||
|
|
|
@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "media/view/media_view_pip.h"
|
#include "media/view/media_view_pip.h"
|
||||||
#include "base/platform/base_platform_info.h"
|
#include "base/platform/base_platform_info.h"
|
||||||
#include "webrtc/webrtc_video_track.h"
|
#include "webrtc/webrtc_video_track.h"
|
||||||
|
#include "ui/integration.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/abstract_button.h"
|
#include "ui/abstract_button.h"
|
||||||
#include "ui/gl/gl_surface.h"
|
#include "ui/gl/gl_surface.h"
|
||||||
|
|
|
@ -460,7 +460,8 @@ void Viewport::RendererGL::validateUserpicFrame(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto size = tile->trackOrUserpicSize();
|
const auto size = tile->trackOrUserpicSize();
|
||||||
tileData.userpicFrame = tile->row()->peer()->generateUserpicImage(
|
tileData.userpicFrame = PeerData::GenerateUserpicImage(
|
||||||
|
tile->row()->peer(),
|
||||||
tile->row()->ensureUserpicView(),
|
tile->row()->ensureUserpicView(),
|
||||||
size.width(),
|
size.width(),
|
||||||
0);
|
0);
|
||||||
|
|
|
@ -77,7 +77,8 @@ void Viewport::RendererSW::validateUserpicFrame(
|
||||||
}
|
}
|
||||||
const auto size = tile->trackOrUserpicSize();
|
const auto size = tile->trackOrUserpicSize();
|
||||||
data.userpicFrame = Images::BlurLargeImage(
|
data.userpicFrame = Images::BlurLargeImage(
|
||||||
tile->row()->peer()->generateUserpicImage(
|
PeerData::GenerateUserpicImage(
|
||||||
|
tile->row()->peer(),
|
||||||
tile->row()->ensureUserpicView(),
|
tile->row()->ensureUserpicView(),
|
||||||
size.width(),
|
size.width(),
|
||||||
0),
|
0),
|
||||||
|
|
250
Telegram/SourceFiles/calls/ui/calls_device_menu.cpp
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
/*
|
||||||
|
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 "calls/ui/calls_device_menu.h"
|
||||||
|
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
|
#include "ui/widgets/menu/menu_item_base.h"
|
||||||
|
#include "ui/widgets/checkbox.h"
|
||||||
|
#include "ui/widgets/labels.h"
|
||||||
|
#include "ui/widgets/popup_menu.h"
|
||||||
|
#include "ui/widgets/scroll_area.h"
|
||||||
|
#include "ui/wrap/vertical_layout.h"
|
||||||
|
#include "webrtc/webrtc_device_common.h"
|
||||||
|
#include "webrtc/webrtc_environment.h"
|
||||||
|
#include "styles/style_calls.h"
|
||||||
|
#include "styles/style_layers.h"
|
||||||
|
|
||||||
|
namespace Calls {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class Subsection final : public Ui::Menu::ItemBase {
|
||||||
|
public:
|
||||||
|
Subsection(
|
||||||
|
not_null<RpWidget*> parent,
|
||||||
|
const style::Menu &st,
|
||||||
|
const QString &text);
|
||||||
|
|
||||||
|
not_null<QAction*> action() const override;
|
||||||
|
bool isEnabled() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int contentHeight() const override;
|
||||||
|
|
||||||
|
const style::Menu &_st;
|
||||||
|
const base::unique_qptr<Ui::FlatLabel> _text;
|
||||||
|
const not_null<QAction*> _dummyAction;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class Selector final : public Ui::Menu::ItemBase {
|
||||||
|
public:
|
||||||
|
Selector(
|
||||||
|
not_null<RpWidget*> parent,
|
||||||
|
const style::Menu &st,
|
||||||
|
rpl::producer<std::vector<Webrtc::DeviceInfo>> devices,
|
||||||
|
rpl::producer<Webrtc::DeviceResolvedId> chosen,
|
||||||
|
Fn<void(QString)> selected);
|
||||||
|
|
||||||
|
not_null<QAction*> action() const override;
|
||||||
|
bool isEnabled() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int contentHeight() const override;
|
||||||
|
[[nodiscard]] int registerId(const QString &id);
|
||||||
|
|
||||||
|
const base::unique_qptr<Ui::ScrollArea> _scroll;
|
||||||
|
const not_null<Ui::VerticalLayout*> _list;
|
||||||
|
const not_null<QAction*> _dummyAction;
|
||||||
|
|
||||||
|
base::flat_map<QString, int> _ids;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Subsection::Subsection(
|
||||||
|
not_null<RpWidget*> parent,
|
||||||
|
const style::Menu &st,
|
||||||
|
const QString &text)
|
||||||
|
: Ui::Menu::ItemBase(parent, st)
|
||||||
|
, _st(st)
|
||||||
|
, _text(base::make_unique_q<Ui::FlatLabel>(
|
||||||
|
this,
|
||||||
|
text,
|
||||||
|
st::callDeviceSelectionLabel))
|
||||||
|
, _dummyAction(new QAction(parent)) {
|
||||||
|
setPointerCursor(false);
|
||||||
|
|
||||||
|
initResizeHook(parent->sizeValue());
|
||||||
|
|
||||||
|
_text->resizeToWidth(st::callDeviceSelectionLabel.minWidth);
|
||||||
|
_text->moveToLeft(st.itemPadding.left(), st.itemPadding.top());
|
||||||
|
}
|
||||||
|
|
||||||
|
not_null<QAction*> Subsection::action() const {
|
||||||
|
return _dummyAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Subsection::isEnabled() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Subsection::contentHeight() const {
|
||||||
|
return _st.itemPadding.top()
|
||||||
|
+ _text->height()
|
||||||
|
+ _st.itemPadding.bottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
Selector::Selector(
|
||||||
|
not_null<RpWidget*> parent,
|
||||||
|
const style::Menu &st,
|
||||||
|
rpl::producer<std::vector<Webrtc::DeviceInfo>> devices,
|
||||||
|
rpl::producer<Webrtc::DeviceResolvedId> chosen,
|
||||||
|
Fn<void(QString)> selected)
|
||||||
|
: Ui::Menu::ItemBase(parent, st)
|
||||||
|
, _scroll(base::make_unique_q<Ui::ScrollArea>(this))
|
||||||
|
, _list(_scroll->setOwnedWidget(object_ptr<Ui::VerticalLayout>(this)))
|
||||||
|
, _dummyAction(new QAction(parent)) {
|
||||||
|
setPointerCursor(false);
|
||||||
|
|
||||||
|
initResizeHook(parent->sizeValue());
|
||||||
|
|
||||||
|
const auto padding = st.itemPadding;
|
||||||
|
const auto group = std::make_shared<Ui::RadiobuttonGroup>();
|
||||||
|
std::move(
|
||||||
|
chosen
|
||||||
|
) | rpl::start_with_next([=](Webrtc::DeviceResolvedId id) {
|
||||||
|
const auto value = id.isDefault() ? 0 : registerId(id.value);
|
||||||
|
if (!group->hasValue() || group->current() != value) {
|
||||||
|
group->setValue(value);
|
||||||
|
}
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
|
group->setChangedCallback([=](int value) {
|
||||||
|
if (value == 0) {
|
||||||
|
selected({});
|
||||||
|
} else {
|
||||||
|
for (const auto &[id, index] : _ids) {
|
||||||
|
if (index == value) {
|
||||||
|
selected(id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
std::move(
|
||||||
|
devices
|
||||||
|
) | rpl::start_with_next([=](const std::vector<Webrtc::DeviceInfo> &v) {
|
||||||
|
while (_list->count()) {
|
||||||
|
delete _list->widgetAt(0);
|
||||||
|
}
|
||||||
|
_list->add(
|
||||||
|
object_ptr<Ui::Radiobutton>(
|
||||||
|
_list.get(),
|
||||||
|
group,
|
||||||
|
0,
|
||||||
|
tr::lng_settings_call_device_default(tr::now),
|
||||||
|
st::groupCallCheckbox,
|
||||||
|
st::groupCallRadio),
|
||||||
|
padding);
|
||||||
|
for (const auto &device : v) {
|
||||||
|
if (device.inactive) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_list->add(
|
||||||
|
object_ptr<Ui::Radiobutton>(
|
||||||
|
_list.get(),
|
||||||
|
group,
|
||||||
|
registerId(device.id),
|
||||||
|
device.name,
|
||||||
|
st::groupCallCheckbox,
|
||||||
|
st::groupCallRadio),
|
||||||
|
padding);
|
||||||
|
}
|
||||||
|
resize(width(), contentHeight());
|
||||||
|
}, lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
not_null<QAction*> Selector::action() const {
|
||||||
|
return _dummyAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Selector::isEnabled() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Selector::contentHeight() const {
|
||||||
|
_list->resizeToWidth(width());
|
||||||
|
if (_list->count() <= 3) {
|
||||||
|
_scroll->resize(width(), _list->height());
|
||||||
|
} else {
|
||||||
|
_scroll->resize(
|
||||||
|
width(),
|
||||||
|
3.5 * st::defaultRadio.diameter);
|
||||||
|
}
|
||||||
|
return _scroll->height();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Selector::registerId(const QString &id) {
|
||||||
|
auto &result = _ids[id];
|
||||||
|
if (!result) {
|
||||||
|
result = int(_ids.size());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddDeviceSelection(
|
||||||
|
not_null<Ui::PopupMenu*> menu,
|
||||||
|
not_null<Webrtc::Environment*> environment,
|
||||||
|
DeviceSelection type,
|
||||||
|
Fn<void(QString)> selected) {
|
||||||
|
const auto title = [&] {
|
||||||
|
switch (type.type) {
|
||||||
|
case Webrtc::DeviceType::Camera:
|
||||||
|
return tr::lng_settings_call_camera(tr::now);
|
||||||
|
case Webrtc::DeviceType::Playback:
|
||||||
|
return tr::lng_settings_call_section_output(tr::now);
|
||||||
|
case Webrtc::DeviceType::Capture:
|
||||||
|
return tr::lng_settings_call_section_input(tr::now);
|
||||||
|
}
|
||||||
|
Unexpected("Type in AddDeviceSelection.");
|
||||||
|
}();
|
||||||
|
menu->addAction(
|
||||||
|
base::make_unique_q<Subsection>(menu, menu->st().menu, title));
|
||||||
|
menu->addAction(
|
||||||
|
base::make_unique_q<Selector>(
|
||||||
|
menu,
|
||||||
|
menu->st().menu,
|
||||||
|
environment->devicesValue(type.type),
|
||||||
|
std::move(type.chosen),
|
||||||
|
selected));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
base::unique_qptr<Ui::PopupMenu> MakeDeviceSelectionMenu(
|
||||||
|
not_null<Ui::RpWidget*> parent,
|
||||||
|
not_null<Webrtc::Environment*> environment,
|
||||||
|
std::vector<DeviceSelection> types,
|
||||||
|
Fn<void(Webrtc::DeviceType, QString)> choose) {
|
||||||
|
auto result = base::make_unique_q<Ui::PopupMenu>(
|
||||||
|
parent,
|
||||||
|
st::callDeviceSelectionMenu);
|
||||||
|
const auto raw = result.get();
|
||||||
|
for (auto type : types) {
|
||||||
|
if (!raw->empty()) {
|
||||||
|
raw->addSeparator();
|
||||||
|
}
|
||||||
|
const auto selected = [=, type = type.type](QString id) {
|
||||||
|
choose(type, id);
|
||||||
|
};
|
||||||
|
AddDeviceSelection(raw, environment, std::move(type), selected);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Calls
|
36
Telegram/SourceFiles/calls/ui/calls_device_menu.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/unique_qptr.h"
|
||||||
|
|
||||||
|
namespace Webrtc {
|
||||||
|
class Environment;
|
||||||
|
struct DeviceResolvedId;
|
||||||
|
enum class DeviceType : uchar;
|
||||||
|
} // namespace Webrtc
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class RpWidget;
|
||||||
|
class PopupMenu;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Calls {
|
||||||
|
|
||||||
|
struct DeviceSelection {
|
||||||
|
Webrtc::DeviceType type;
|
||||||
|
rpl::producer<Webrtc::DeviceResolvedId> chosen;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] base::unique_qptr<Ui::PopupMenu> MakeDeviceSelectionMenu(
|
||||||
|
not_null<Ui::RpWidget*> parent,
|
||||||
|
not_null<Webrtc::Environment*> environment,
|
||||||
|
std::vector<DeviceSelection> types,
|
||||||
|
Fn<void(Webrtc::DeviceType, QString)> choose);
|
||||||
|
|
||||||
|
} // namespace Calls
|
|
@ -7,18 +7,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "chat_helpers/bot_keyboard.h"
|
#include "chat_helpers/bot_keyboard.h"
|
||||||
|
|
||||||
|
#include "api/api_bot.h"
|
||||||
#include "core/click_handler_types.h"
|
#include "core/click_handler_types.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_user.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/history_item_components.h"
|
#include "history/history_item_components.h"
|
||||||
#include "data/data_user.h"
|
|
||||||
#include "data/data_session.h"
|
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "window/window_session_controller.h"
|
|
||||||
#include "ui/cached_round_corners.h"
|
#include "ui/cached_round_corners.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "api/api_bot.h"
|
#include "ui/ui_utility.h"
|
||||||
#include "styles/style_widgets.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "styles/style_chat.h"
|
#include "styles/style_chat.h"
|
||||||
|
#include "styles/style_widgets.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
|
@ -296,7 +296,9 @@ emojiPanButton: RoundButton(defaultActiveButton) {
|
||||||
textTop: 2px;
|
textTop: 2px;
|
||||||
}
|
}
|
||||||
emojiPanExpand: RoundButton(defaultActiveButton) {
|
emojiPanExpand: RoundButton(defaultActiveButton) {
|
||||||
font: font(12px bold);
|
style: TextStyle(semiboldTextStyle) {
|
||||||
|
font: font(12px bold);
|
||||||
|
}
|
||||||
width: -8px;
|
width: -8px;
|
||||||
height: 19px;
|
height: 19px;
|
||||||
textTop: 1px;
|
textTop: 1px;
|
||||||
|
@ -1499,5 +1501,11 @@ pickLocationChooseOnMap: RoundButton(defaultActiveButton) {
|
||||||
height: 44px;
|
height: 44px;
|
||||||
textTop: 11px;
|
textTop: 11px;
|
||||||
width: -96px;
|
width: -96px;
|
||||||
font: font(15px semibold);
|
style: TextStyle(semiboldTextStyle) {
|
||||||
|
font: font(15px semibold);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendGifBox: Box(defaultBox) {
|
||||||
|
shadowIgnoreBottomSkip: true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1450,17 +1450,17 @@ void EmojiListWidget::drawCollapsedBadge(
|
||||||
int count) {
|
int count) {
|
||||||
const auto &st = st::emojiPanExpand;
|
const auto &st = st::emojiPanExpand;
|
||||||
const auto text = u"+%1"_q.arg(count - _columnCount * kCollapsedRows + 1);
|
const auto text = u"+%1"_q.arg(count - _columnCount * kCollapsedRows + 1);
|
||||||
const auto textWidth = st.font->width(text);
|
const auto textWidth = st.style.font->width(text);
|
||||||
const auto buttonw = std::max(textWidth - st.width, st.height);
|
const auto buttonw = std::max(textWidth - st.width, st.height);
|
||||||
const auto buttonh = st.height;
|
const auto buttonh = st.height;
|
||||||
const auto buttonx = position.x() + (_singleSize.width() - buttonw) / 2;
|
const auto buttonx = position.x() + (_singleSize.width() - buttonw) / 2;
|
||||||
const auto buttony = position.y() + (_singleSize.height() - buttonh) / 2;
|
const auto buttony = position.y() + (_singleSize.height() - buttonh) / 2;
|
||||||
_collapsedBg.paint(p, QRect(buttonx, buttony, buttonw, buttonh));
|
_collapsedBg.paint(p, QRect(buttonx, buttony, buttonw, buttonh));
|
||||||
p.setPen(this->st().bg);
|
p.setPen(this->st().bg);
|
||||||
p.setFont(st.font);
|
p.setFont(st.style.font);
|
||||||
p.drawText(
|
p.drawText(
|
||||||
buttonx + (buttonw - textWidth) / 2,
|
buttonx + (buttonw - textWidth) / 2,
|
||||||
(buttony + st.textTop + st.font->ascent),
|
(buttony + st.textTop + st.style.font->ascent),
|
||||||
text);
|
text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2546,12 +2546,12 @@ int EmojiListWidget::paintButtonGetWidth(
|
||||||
: selected
|
: selected
|
||||||
? st::emojiPanButton.textFgOver
|
? st::emojiPanButton.textFgOver
|
||||||
: st::emojiPanButton.textFg);
|
: st::emojiPanButton.textFg);
|
||||||
p.setFont(st::emojiPanButton.font);
|
p.setFont(st::emojiPanButton.style.font);
|
||||||
p.drawText(
|
p.drawText(
|
||||||
rect.x() - (st::emojiPanButton.width / 2),
|
rect.x() - (st::emojiPanButton.width / 2),
|
||||||
(rect.y()
|
(rect.y()
|
||||||
+ st::emojiPanButton.textTop
|
+ st::emojiPanButton.textTop
|
||||||
+ st::emojiPanButton.font->ascent),
|
+ st::emojiPanButton.style.font->ascent),
|
||||||
button.text);
|
button.text);
|
||||||
return emojiRight() - rect.x();
|
return emojiRight() - rect.x();
|
||||||
}
|
}
|
||||||
|
@ -2678,7 +2678,7 @@ void EmojiListWidget::initButton(
|
||||||
const QString &text,
|
const QString &text,
|
||||||
bool gradient) {
|
bool gradient) {
|
||||||
button.text = text;
|
button.text = text;
|
||||||
button.textWidth = st::emojiPanButton.font->width(text);
|
button.textWidth = st::emojiPanButton.style.font->width(text);
|
||||||
const auto width = button.textWidth - st::emojiPanButton.width;
|
const auto width = button.textWidth - st::emojiPanButton.width;
|
||||||
const auto height = st::emojiPanButton.height;
|
const auto height = st::emojiPanButton.height;
|
||||||
const auto factor = style::DevicePixelRatio();
|
const auto factor = style::DevicePixelRatio();
|
||||||
|
|
|
@ -46,6 +46,7 @@ enum class Section;
|
||||||
} // namespace Ui::Emoji
|
} // namespace Ui::Emoji
|
||||||
|
|
||||||
namespace Ui::Text {
|
namespace Ui::Text {
|
||||||
|
class CustomEmoji;
|
||||||
struct CustomEmojiPaintContext;
|
struct CustomEmojiPaintContext;
|
||||||
} // namespace Ui::Text
|
} // namespace Ui::Text
|
||||||
|
|
||||||
|
|
|
@ -24,12 +24,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "mtproto/mtproto_config.h"
|
#include "mtproto/mtproto_config.h"
|
||||||
#include "core/click_handler_types.h"
|
#include "core/click_handler_types.h"
|
||||||
#include "ui/controls/tabbed_search.h"
|
#include "ui/controls/tabbed_search.h"
|
||||||
|
#include "ui/layers/generic_box.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/fields/input_field.h"
|
#include "ui/widgets/fields/input_field.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "ui/effects/ripple_animation.h"
|
#include "ui/effects/ripple_animation.h"
|
||||||
#include "ui/image/image.h"
|
#include "ui/image/image.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
|
#include "boxes/send_gif_with_caption_box.h"
|
||||||
#include "boxes/stickers_box.h"
|
#include "boxes/stickers_box.h"
|
||||||
#include "inline_bots/inline_bot_result.h"
|
#include "inline_bots/inline_bot_result.h"
|
||||||
#include "storage/localstorage.h"
|
#include "storage/localstorage.h"
|
||||||
|
@ -414,6 +416,22 @@ base::unique_qptr<Ui::PopupMenu> GifsListWidget::fillContextMenu(
|
||||||
SendMenu::DefaultCallback(_show, send),
|
SendMenu::DefaultCallback(_show, send),
|
||||||
icons);
|
icons);
|
||||||
|
|
||||||
|
if (!isInlineResult) {
|
||||||
|
auto done = crl::guard(this, [=](
|
||||||
|
Api::SendOptions options,
|
||||||
|
TextWithTags text) {
|
||||||
|
selectInlineResult(selected, options, true, std::move(text));
|
||||||
|
});
|
||||||
|
const auto show = _show;
|
||||||
|
menu->addAction(tr::lng_send_gif_with_caption(tr::now), [=] {
|
||||||
|
show->show(Box(
|
||||||
|
Ui::SendGifWithCaptionBox,
|
||||||
|
item->getDocument(),
|
||||||
|
copyDetails,
|
||||||
|
std::move(done)));
|
||||||
|
}, &st::menuIconEdit);
|
||||||
|
}
|
||||||
|
|
||||||
if (const auto item = _mosaic.maybeItemAt(_selected)) {
|
if (const auto item = _mosaic.maybeItemAt(_selected)) {
|
||||||
const auto document = item->getDocument()
|
const auto document = item->getDocument()
|
||||||
? item->getDocument() // Saved GIF.
|
? item->getDocument() // Saved GIF.
|
||||||
|
@ -464,7 +482,8 @@ void GifsListWidget::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
void GifsListWidget::selectInlineResult(
|
void GifsListWidget::selectInlineResult(
|
||||||
int index,
|
int index,
|
||||||
Api::SendOptions options,
|
Api::SendOptions options,
|
||||||
bool forceSend) {
|
bool forceSend,
|
||||||
|
TextWithTags caption) {
|
||||||
const auto item = _mosaic.maybeItemAt(index);
|
const auto item = _mosaic.maybeItemAt(index);
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
|
@ -527,6 +546,7 @@ void GifsListWidget::selectInlineResult(
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
sendGIFCallback();
|
sendGIFCallback();
|
||||||
|
.caption = std::move(caption),
|
||||||
}
|
}
|
||||||
} else if (!preview.usingThumbnail()) {
|
} else if (!preview.usingThumbnail()) {
|
||||||
if (preview.loading()) {
|
if (preview.loading()) {
|
||||||
|
|
|
@ -172,7 +172,8 @@ private:
|
||||||
void selectInlineResult(
|
void selectInlineResult(
|
||||||
int index,
|
int index,
|
||||||
Api::SendOptions options,
|
Api::SendOptions options,
|
||||||
bool forceSend = false);
|
bool forceSend = false,
|
||||||
|
TextWithTags caption = {});
|
||||||
|
|
||||||
const std::shared_ptr<Show> _show;
|
const std::shared_ptr<Show> _show;
|
||||||
std::unique_ptr<Ui::TabbedSearch> _search;
|
std::unique_ptr<Ui::TabbedSearch> _search;
|
||||||
|
|
|
@ -1063,10 +1063,26 @@ base::unique_qptr<Ui::RpWidget> CreateDisabledFieldView(
|
||||||
st::historySendDisabled);
|
st::historySendDisabled);
|
||||||
label->setAttribute(Qt::WA_TransparentForMouseEvents);
|
label->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
raw->setPointerCursor(false);
|
raw->setPointerCursor(false);
|
||||||
|
|
||||||
|
const auto &st = st::historyComposeField;
|
||||||
|
|
||||||
|
const auto metrics = QFontMetricsF(st.style.font->f);
|
||||||
|
const auto realAscent = int(base::SafeRound(metrics.ascent()));
|
||||||
|
const auto ascentAdd = st.style.font->ascent - realAscent;
|
||||||
|
const auto customFontMarginTop = ascentAdd;
|
||||||
|
const auto leading = qMax(metrics.leading(), qreal(0.0));
|
||||||
|
const auto adjustment = (metrics.ascent() + leading)
|
||||||
|
- ((st.style.font->height * 4) / 5);
|
||||||
|
const auto placeholderCustomFontSkip = int(base::SafeRound(-adjustment));
|
||||||
|
|
||||||
|
const auto margins = st.textMargins
|
||||||
|
+ st.placeholderMargins
|
||||||
|
+ QMargins(0, style::ConvertScale(4)
|
||||||
|
+ placeholderCustomFontSkip
|
||||||
|
+ customFontMarginTop, 0, 0);
|
||||||
|
|
||||||
raw->widthValue(
|
raw->widthValue(
|
||||||
) | rpl::start_with_next([=](int width) {
|
) | rpl::start_with_next([=](int width) {
|
||||||
const auto &st = st::historyComposeField;
|
|
||||||
const auto margins = (st.textMargins + st.placeholderMargins);
|
|
||||||
const auto available = width - margins.left() - margins.right();
|
const auto available = width - margins.left() - margins.right();
|
||||||
const auto skip = st::historySendDisabledIconSkip;
|
const auto skip = st::historySendDisabledIconSkip;
|
||||||
label->resizeToWidth(available - skip);
|
label->resizeToWidth(available - skip);
|
||||||
|
@ -1075,8 +1091,6 @@ base::unique_qptr<Ui::RpWidget> CreateDisabledFieldView(
|
||||||
raw->paintRequest(
|
raw->paintRequest(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
auto p = QPainter(raw);
|
auto p = QPainter(raw);
|
||||||
const auto &st = st::historyComposeField;
|
|
||||||
const auto margins = (st.textMargins + st.placeholderMargins);
|
|
||||||
const auto &icon = st::historySendDisabledIcon;
|
const auto &icon = st::historySendDisabledIcon;
|
||||||
icon.paint(
|
icon.paint(
|
||||||
p,
|
p,
|
||||||
|
|
|
@ -20,6 +20,10 @@ class InputField;
|
||||||
class CrossButton;
|
class CrossButton;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Ui::Text {
|
||||||
|
class CustomEmoji;
|
||||||
|
} // namespace Ui::Text
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
class StickersSet;
|
class StickersSet;
|
||||||
class StickersSetThumbnailView;
|
class StickersSetThumbnailView;
|
||||||
|
|
|
@ -220,11 +220,14 @@ StickersListWidget::StickersListWidget(
|
||||||
st().pathBg,
|
st().pathBg,
|
||||||
st().pathFg,
|
st().pathFg,
|
||||||
[=] { update(); }))
|
[=] { update(); }))
|
||||||
, _megagroupSetAbout(st::columnMinimalWidthThird - st::emojiScroll.width - st().headerLeft)
|
, _megagroupSetAbout(st::columnMinimalWidthThird
|
||||||
|
- st::emojiScroll.width
|
||||||
|
- st().headerLeft)
|
||||||
, _addText(tr::lng_stickers_featured_add(tr::now))
|
, _addText(tr::lng_stickers_featured_add(tr::now))
|
||||||
, _addWidth(st::stickersTrendingAdd.font->width(_addText))
|
, _addWidth(st::stickersTrendingAdd.style.font->width(_addText))
|
||||||
, _installedText(tr::lng_stickers_featured_installed(tr::now))
|
, _installedText(tr::lng_stickers_featured_installed(tr::now))
|
||||||
, _installedWidth(st::stickersTrendingInstalled.font->width(_installedText))
|
, _installedWidth(
|
||||||
|
st::stickersTrendingInstalled.style.font->width(_installedText))
|
||||||
, _settings(this, tr::lng_stickers_you_have(tr::now))
|
, _settings(this, tr::lng_stickers_you_have(tr::now))
|
||||||
, _previewTimer([=] { showPreview(); })
|
, _previewTimer([=] { showPreview(); })
|
||||||
, _premiumMark(std::make_unique<StickerPremiumMark>(
|
, _premiumMark(std::make_unique<StickerPremiumMark>(
|
||||||
|
@ -981,7 +984,7 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
|
||||||
const auto &st = installedSet
|
const auto &st = installedSet
|
||||||
? st::stickersTrendingInstalled
|
? st::stickersTrendingInstalled
|
||||||
: st::stickersTrendingAdd;
|
: st::stickersTrendingAdd;
|
||||||
p.setFont(st.font);
|
p.setFont(st.style.font);
|
||||||
p.setPen(selected ? st.textFgOver : st.textFg);
|
p.setPen(selected ? st.textFgOver : st.textFg);
|
||||||
p.drawTextLeft(
|
p.drawTextLeft(
|
||||||
add.x() - (st.width / 2),
|
add.x() - (st.width / 2),
|
||||||
|
@ -1245,7 +1248,7 @@ void StickersListWidget::paintMegagroupEmptySet(Painter &p, int y, bool buttonSe
|
||||||
_megagroupSetButtonRipple.reset();
|
_megagroupSetButtonRipple.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.setFont(st::stickerGroupCategoryAdd.font);
|
p.setFont(st::stickerGroupCategoryAdd.style.font);
|
||||||
p.setPen(buttonSelected ? st::stickerGroupCategoryAdd.textFgOver : st::stickerGroupCategoryAdd.textFg);
|
p.setPen(buttonSelected ? st::stickerGroupCategoryAdd.textFgOver : st::stickerGroupCategoryAdd.textFg);
|
||||||
p.drawTextLeft(button.x() - (st::stickerGroupCategoryAdd.width / 2), button.y() + st::stickerGroupCategoryAdd.textTop, width(), _megagroupSetButtonText, _megagroupSetButtonTextWidth);
|
p.drawTextLeft(button.x() - (st::stickerGroupCategoryAdd.width / 2), button.y() + st::stickerGroupCategoryAdd.textTop, width(), _megagroupSetButtonText, _megagroupSetButtonTextWidth);
|
||||||
}
|
}
|
||||||
|
@ -2779,7 +2782,7 @@ void StickersListWidget::refreshMegagroupSetGeometry() {
|
||||||
auto left = megagroupSetInfoLeft();
|
auto left = megagroupSetInfoLeft();
|
||||||
auto availableWidth = (width() - left);
|
auto availableWidth = (width() - left);
|
||||||
auto top = _megagroupSetAbout.countHeight(availableWidth) + st::stickerGroupCategoryAddMargin.top();
|
auto top = _megagroupSetAbout.countHeight(availableWidth) + st::stickerGroupCategoryAddMargin.top();
|
||||||
_megagroupSetButtonTextWidth = st::stickerGroupCategoryAdd.font->width(_megagroupSetButtonText);
|
_megagroupSetButtonTextWidth = st::stickerGroupCategoryAdd.style.font->width(_megagroupSetButtonText);
|
||||||
auto buttonWidth = _megagroupSetButtonTextWidth - st::stickerGroupCategoryAdd.width;
|
auto buttonWidth = _megagroupSetButtonTextWidth - st::stickerGroupCategoryAdd.width;
|
||||||
_megagroupSetButtonRect = QRect(left, top, buttonWidth, st::stickerGroupCategoryAdd.height);
|
_megagroupSetButtonRect = QRect(left, top, buttonWidth, st::stickerGroupCategoryAdd.height);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "chat_helpers/tabbed_section.h"
|
#include "chat_helpers/tabbed_section.h"
|
||||||
|
|
||||||
#include "chat_helpers/tabbed_selector.h"
|
#include "chat_helpers/tabbed_selector.h"
|
||||||
|
#include "ui/ui_utility.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "styles/style_chat_helpers.h"
|
#include "styles/style_chat_helpers.h"
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/image/image_prepare.h"
|
#include "ui/image/image_prepare.h"
|
||||||
#include "ui/cached_round_corners.h"
|
#include "ui/cached_round_corners.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
|
#include "ui/ui_utility.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "main/main_session_settings.h"
|
#include "main/main_session_settings.h"
|
||||||
|
|
|
@ -62,6 +62,7 @@ struct FileChosen {
|
||||||
not_null<DocumentData*> document;
|
not_null<DocumentData*> document;
|
||||||
Api::SendOptions options;
|
Api::SendOptions options;
|
||||||
Ui::MessageSendingAnimationFrom messageSendingFrom;
|
Ui::MessageSendingAnimationFrom messageSendingFrom;
|
||||||
|
TextWithTags caption;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PhotoChosen {
|
struct PhotoChosen {
|
||||||
|
|
|
@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
#include "ui/widgets/tooltip.h"
|
#include "ui/widgets/tooltip.h"
|
||||||
|
#include "ui/ui_utility.h"
|
||||||
#include "window/section_widget.h" // Window::ChatThemeValueFromPeer.
|
#include "window/section_widget.h" // Window::ChatThemeValueFromPeer.
|
||||||
#include "window/themes/window_theme.h"
|
#include "window/themes/window_theme.h"
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
|
|