Updated to 6.0.3.
Esse commit está contido em:
@@ -4,8 +4,8 @@ on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '!docs/building-win*.md'
|
||||
- '**.md'
|
||||
- '!docs/building-win*.md'
|
||||
- 'changelog.txt'
|
||||
- 'LEGAL'
|
||||
- 'LICENSE'
|
||||
@@ -23,8 +23,8 @@ on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '!docs/building-win*.md'
|
||||
- '**.md'
|
||||
- '!docs/building-win*.md'
|
||||
- 'changelog.txt'
|
||||
- 'LEGAL'
|
||||
- 'LICENSE'
|
||||
|
||||
@@ -529,6 +529,8 @@ PRIVATE
|
||||
data/notify/data_notify_settings.h
|
||||
data/notify/data_peer_notify_settings.cpp
|
||||
data/notify/data_peer_notify_settings.h
|
||||
data/notify/data_peer_notify_volume.cpp
|
||||
data/notify/data_peer_notify_volume.h
|
||||
data/stickers/data_custom_emoji.cpp
|
||||
data/stickers/data_custom_emoji.h
|
||||
data/stickers/data_stickers_set.cpp
|
||||
@@ -886,6 +888,8 @@ PRIVATE
|
||||
history/view/history_view_fake_items.h
|
||||
history/view/history_view_group_call_bar.cpp
|
||||
history/view/history_view_group_call_bar.h
|
||||
history/view/history_view_group_members_widget.cpp
|
||||
history/view/history_view_group_members_widget.h
|
||||
history/view/history_view_item_preview.h
|
||||
history/view/history_view_list_widget.cpp
|
||||
history/view/history_view_list_widget.h
|
||||
@@ -1129,6 +1133,8 @@ PRIVATE
|
||||
inline_bots/inline_results_widget.h
|
||||
intro/intro_code.cpp
|
||||
intro/intro_code.h
|
||||
intro/intro_email.cpp
|
||||
intro/intro_email.h
|
||||
intro/intro_password_check.cpp
|
||||
intro/intro_password_check.h
|
||||
intro/intro_phone.cpp
|
||||
@@ -1284,6 +1290,8 @@ PRIVATE
|
||||
menu/menu_antispam_validator.h
|
||||
menu/menu_item_download_files.cpp
|
||||
menu/menu_item_download_files.h
|
||||
menu/menu_item_rate_transcribe_session.cpp
|
||||
menu/menu_item_rate_transcribe_session.h
|
||||
menu/menu_mute.cpp
|
||||
menu/menu_mute.h
|
||||
menu/menu_send.cpp
|
||||
@@ -1317,6 +1325,8 @@ PRIVATE
|
||||
mtproto/special_config_request.cpp
|
||||
mtproto/special_config_request.h
|
||||
mtproto/type_utils.h
|
||||
overview/overview_checkbox.cpp
|
||||
overview/overview_checkbox.h
|
||||
overview/overview_layout.cpp
|
||||
overview/overview_layout.h
|
||||
overview/overview_layout_delegate.h
|
||||
@@ -1437,10 +1447,6 @@ PRIVATE
|
||||
platform/platform_window_title.h
|
||||
profile/profile_back_button.cpp
|
||||
profile/profile_back_button.h
|
||||
profile/profile_block_group_members.cpp
|
||||
profile/profile_block_group_members.h
|
||||
profile/profile_block_peer_list.cpp
|
||||
profile/profile_block_peer_list.h
|
||||
profile/profile_block_widget.cpp
|
||||
profile/profile_block_widget.h
|
||||
profile/profile_cover_drop_area.cpp
|
||||
@@ -1637,8 +1643,6 @@ PRIVATE
|
||||
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.h
|
||||
ui/widgets/chat_filters_tabs_strip.cpp
|
||||
ui/widgets/chat_filters_tabs_strip.h
|
||||
ui/widgets/peer_bubble.cpp
|
||||
@@ -1671,6 +1675,8 @@ PRIVATE
|
||||
window/window_adaptive.h
|
||||
window/window_chat_preview.cpp
|
||||
window/window_chat_preview.h
|
||||
window/window_chat_switch_process.cpp
|
||||
window/window_chat_switch_process.h
|
||||
window/window_connecting_widget.cpp
|
||||
window/window_connecting_widget.h
|
||||
window/window_controller.cpp
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Icon / Mini / mini_ton_bold</title>
|
||||
<g id="Icon-/-Mini-/-mini_ton_bold" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M12.5980305,2.7875 C13.2201476,2.7875 13.7244732,3.29182558 13.7244732,3.9139427 C13.7244732,4.11165014 13.6724374,4.30587532 13.5735947,4.47710133 L9.21676388,12.0244744 C8.80179975,12.7433201 7.88266529,12.9896647 7.16381961,12.5747006 C6.92829269,12.4387393 6.73407151,12.2414175 6.60185728,12.0037668 L2.40584723,4.46158062 C2.10339516,3.91793325 2.29892259,3.23203413 2.84256996,2.92958206 C3.01005587,2.83640316 3.19854713,2.7875 3.39020787,2.7875 L12.5980305,2.7875 Z M7.24956057,4.2875 L4.025,4.2875 L7.24956057,10.0835 L7.24956057,4.2875 Z M11.95,4.2875 L8.74956057,4.2875 L8.74956057,9.8255 L11.95,4.2875 Z" id="Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
Depois Largura: | Altura: | Tamanho: 1.0 KiB |
@@ -380,6 +380,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_intro_fragment_about" = "Get the code for {phone_number} in the Anonymous Numbers section on Fragment.";
|
||||
"lng_intro_fragment_button" = "Open Fragment";
|
||||
|
||||
"lng_intro_email_setup_title" = "Choose a login email";
|
||||
"lng_intro_email_confirm_subtitle" = "Please check your email {email} (don't forget the spam folder) and enter the code we just sent you.";
|
||||
|
||||
"lng_phone_title" = "Your Phone Number";
|
||||
"lng_phone_desc" = "Please confirm your country code\nand enter your phone number.";
|
||||
"lng_phone_to_qr" = "Quick log in using QR code";
|
||||
@@ -547,6 +550,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_notify_global" = "Global settings";
|
||||
"lng_settings_notify_title" = "Notifications for chats";
|
||||
"lng_settings_desktop_notify" = "Desktop notifications";
|
||||
"lng_settings_master_volume_notifications" = "Volume";
|
||||
"lng_settings_native_title" = "System integration";
|
||||
"lng_settings_use_windows" = "Use Windows notifications";
|
||||
"lng_settings_skip_in_focus" = "Respect system Focus mode";
|
||||
@@ -587,12 +591,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_notification_title_private_chats" = "Notifications for private chats";
|
||||
"lng_notification_about_private_chats#one" = "Please note that **{count} chat** is listed as an exception and won't be affected by this change.";
|
||||
"lng_notification_about_private_chats#other" = "Please note that **{count} chats** are listed as exceptions and won't be affected by this change.";
|
||||
"lng_notification_volume_private_chats" = "Notifications volume for private chats";
|
||||
"lng_notification_title_groups" = "Notifications for groups";
|
||||
"lng_notification_about_groups#one" = "Please note that **{count} group** is listed as an exception and won't be affected by this change.";
|
||||
"lng_notification_about_groups#other" = "Please note that **{count} groups** are listed as exceptions and won't be affected by this change.";
|
||||
"lng_notification_volume_groups" = "Notifications volume for groups";
|
||||
"lng_notification_title_channels" = "Notifications for channels";
|
||||
"lng_notification_about_channels#one" = "Please note that **{count} channel** is listed as an exception and won't be affected by this change.";
|
||||
"lng_notification_about_channels#other" = "Please note that **{count} channels** are listed as exceptions and won't be affected by this change.";
|
||||
"lng_notification_volume_channel" = "Notifications volume for channels";
|
||||
"lng_notification_exceptions_view" = "View exceptions";
|
||||
"lng_notification_enable" = "Enable notifications";
|
||||
"lng_notification_sound" = "Sound";
|
||||
@@ -722,6 +729,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_shortcuts_media_fullscreen" = "Toggle video fullscreen";
|
||||
"lng_shortcuts_show_chat_menu" = "Show chat menu";
|
||||
"lng_shortcuts_show_chat_preview" = "Show chat preview";
|
||||
"lng_shortcuts_record_voice_message" = "Record Voice Message";
|
||||
"lng_shortcuts_record_round_message" = "Record Round Message";
|
||||
|
||||
"lng_settings_chat_reactions_title" = "Quick Reaction";
|
||||
"lng_settings_chat_reactions_subtitle" = "Choose your favorite reaction";
|
||||
@@ -1273,6 +1282,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_quick_dialog_action_toast_archive_success" = "The chat has been archived.";
|
||||
"lng_quick_dialog_action_toast_unarchive_success" = "The chat has been unarchived.";
|
||||
|
||||
"lng_archive_hint_title" = "This is your Archive";
|
||||
"lng_archive_hint_about" = "Archived chats will remain in the Archive when you receive a new message. {link}";
|
||||
"lng_archive_hint_about_unmuted" = "When you receive a new message, muted chats will remain in the Archive, while unmuted chats will be moved to Chats. {link}";
|
||||
"lng_archive_hint_about_link" = "Tap to change {emoji}";
|
||||
"lng_archive_hint_section_1" = "Archived Chats";
|
||||
"lng_archive_hint_section_1_info" = "Move any chat into your Archive and back by swiping on it.";
|
||||
"lng_archive_hint_section_2" = "Hiding Archive";
|
||||
"lng_archive_hint_section_2_info" = "Hide the Archive from your Main screen by swiping on it.";
|
||||
"lng_archive_hint_section_3" = "Stories";
|
||||
"lng_archive_hint_section_3_info" = "Archive Stories from your contacts separately from chats with them.";
|
||||
"lng_archive_hint_button" = "Got it";
|
||||
|
||||
"lng_settings_generic_subscribe" = "Subscribe to {link} to use this setting.";
|
||||
"lng_settings_generic_subscribe_link" = "Telegram Premium";
|
||||
|
||||
@@ -2090,6 +2111,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_sure_delete_contact" = "Are you sure you want to delete {contact} from your contact list?";
|
||||
"lng_sure_delete_history" = "Are you sure you want to delete all message history with {contact}?\n\nThis action cannot be undone.";
|
||||
"lng_sure_delete_group_history" = "Are you sure you want to delete all messages in \"{group}\"?\n\nThis action cannot be undone.";
|
||||
"lng_sure_delete_channel_history" = "Are you sure you want to delete all messages in \"{channel}\"?\n\n**This action cannot be undone.**";
|
||||
"lng_sure_delete_and_exit" = "Are you sure you want to delete all message history and leave «{group}»?\n\nThis action cannot be undone.";
|
||||
"lng_sure_leave_channel" = "Are you sure you want to leave\nthis channel?";
|
||||
"lng_sure_delete_channel" = "Are you sure you want to delete this channel? All subscribers will be removed and all messages will be lost.";
|
||||
@@ -2937,6 +2959,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_credits_box_history_entry_ads" = "Ads Platform";
|
||||
"lng_credits_box_history_entry_premium_bot" = "Stars Top-Up";
|
||||
"lng_credits_box_history_entry_currency_in" = "TON Top-Up";
|
||||
"lng_credits_box_history_entry_posts_search" = "Posts Search";
|
||||
"lng_credits_box_history_entry_api" = "Paid Broadcast";
|
||||
"lng_credits_box_history_entry_floodskip_about#one" = "{count} Message";
|
||||
"lng_credits_box_history_entry_floodskip_about#other" = "{count} Messages";
|
||||
@@ -3645,8 +3668,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_hidden_hint_channel" = "This gift is hidden from visitors of your channel.";
|
||||
"lng_gift_visible_hint_channel" = "This gift is visible in your channel's Gifts.";
|
||||
"lng_gift_in_blockchain" = "This gift is in TON blockchain. {link}";
|
||||
"lng_gift_in_blockchain_link" = "View >";
|
||||
"lng_gift_visible_hide" = "Hide >";
|
||||
"lng_gift_in_blockchain_link_arrow" = "View {arrow}";
|
||||
"lng_gift_visible_hide_arrow" = "Hide {arrow}";
|
||||
"lng_gift_visible_show_arrow" = "Show {arrow}";
|
||||
"lng_gift_show_on_page" = "Display on my Page";
|
||||
"lng_gift_show_on_channel" = "Display in channel's Gifts";
|
||||
"lng_gift_availability" = "Availability";
|
||||
@@ -3665,7 +3689,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_unique_address_copied" = "Address copied to clipboard.";
|
||||
"lng_gift_unique_status" = "Status";
|
||||
"lng_gift_unique_status_non" = "Non-Unique";
|
||||
"lng_gift_unique_status_upgrade" = "upgrade";
|
||||
"lng_gift_unique_upgrade" = "Upgrade";
|
||||
"lng_gift_unique_number" = "Collectible #{index}";
|
||||
"lng_gift_unique_number_by" = "Collectible #{index} by {name}";
|
||||
"lng_gift_unique_model" = "Model";
|
||||
@@ -4324,6 +4348,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_archive_to_list" = "Move to chat list";
|
||||
"lng_context_archive_to_menu_info" = "Archive moved to the main menu!\nRight click the archive button to return the Archive to your chat list.";
|
||||
"lng_context_archive_settings" = "Archive settings";
|
||||
"lng_context_archive_how_does_it_work" = "How does it work?";
|
||||
|
||||
"lng_context_mute" = "Mute notifications";
|
||||
"lng_context_unmute" = "Unmute";
|
||||
@@ -4335,6 +4360,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_remove_from_group" = "Remove from group";
|
||||
"lng_context_add_to_group" = "Add to group";
|
||||
|
||||
"lng_context_rate_transcription" = "Rate transcription";
|
||||
"lng_toast_sent_rate_transcription" = "Thank you for your feedback!";
|
||||
|
||||
"lng_context_copy_link" = "Copy Link";
|
||||
"lng_context_copy_message_link" = "Copy Message Link";
|
||||
"lng_context_copy_post_link" = "Copy Post Link";
|
||||
@@ -6372,6 +6400,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_ringtones_box_title" = "Notification Sound";
|
||||
"lng_ringtones_box_cloud_subtitle" = "Choose your tone";
|
||||
"lng_ringtones_box_volume" = "Volume";
|
||||
"lng_ringtones_box_upload_choose" = "Choose a tone";
|
||||
"lng_ringtones_box_upload_button" = "Upload Sound";
|
||||
"lng_ringtones_box_about" = "Right click on any short voice note or MP3 file in chat and select \"Save for Notifications\". It will appear here.";
|
||||
|
||||
@@ -51,8 +51,8 @@ var LocationPicker = {
|
||||
},
|
||||
init: function (params) {
|
||||
mapboxgl.accessToken = params.token;
|
||||
if (params.protocol) {
|
||||
mapboxgl.config.API_URL = params.protocol + '://domain/api.mapbox.com';
|
||||
if (location.hostname != 'desktop-app-resource') {
|
||||
mapboxgl.config.API_URL = location.protocol + '//' + location.host + '/api.mapbox.com';
|
||||
}
|
||||
|
||||
var options = { container: 'map', config: {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="6.0.2.0" />
|
||||
Version="6.0.3.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
||||
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 6,0,2,0
|
||||
PRODUCTVERSION 6,0,2,0
|
||||
FILEVERSION 6,0,3,0
|
||||
PRODUCTVERSION 6,0,3,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -62,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", ""
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "6.0.2.0"
|
||||
VALUE "FileVersion", "6.0.3.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2025"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "6.0.2.0"
|
||||
VALUE "ProductVersion", "6.0.3.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 6,0,2,0
|
||||
PRODUCTVERSION 6,0,2,0
|
||||
FILEVERSION 6,0,3,0
|
||||
PRODUCTVERSION 6,0,3,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -53,10 +53,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", ""
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "6.0.2.0"
|
||||
VALUE "FileVersion", "6.0.3.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2025"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "6.0.2.0"
|
||||
VALUE "ProductVersion", "6.0.3.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -217,11 +217,7 @@ void Authorizations::toggleCallsDisabled(uint64 hash, bool disabled) {
|
||||
MTP_bool(disabled)
|
||||
)).done([=] {
|
||||
_toggleCallsDisabledRequests.remove(hash);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
LOG(("API Error: toggle calls %1. Hash: %2. %3.")
|
||||
.arg(disabled ? u"disabled"_q : u"enabled"_q)
|
||||
.arg(hash)
|
||||
.arg(error.type()));
|
||||
}).fail([=] {
|
||||
_toggleCallsDisabledRequests.remove(hash);
|
||||
}).send();
|
||||
_toggleCallsDisabledRequests.emplace(hash, id);
|
||||
|
||||
@@ -142,13 +142,12 @@ void ConfirmSubscriptionBox(
|
||||
const auto content = box->verticalLayout();
|
||||
|
||||
Ui::AddSkip(content, st::confirmInvitePhotoTop);
|
||||
const auto userpicWrap = content->add(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
content,
|
||||
object_ptr<Ui::RpWidget>(content)));
|
||||
const auto userpic = userpicWrap->entity();
|
||||
const auto userpic = content->add(
|
||||
object_ptr<Ui::RpWidget>(content),
|
||||
style::al_top);
|
||||
const auto photoSize = st::confirmInvitePhotoSize;
|
||||
userpic->resize(Size(photoSize));
|
||||
userpic->setNaturalWidth(photoSize);
|
||||
const auto creditsIconSize = photoSize / 3;
|
||||
const auto creditsIconCallback =
|
||||
Ui::PaintOutlinedColoredCreditsIconCallback(
|
||||
@@ -188,8 +187,8 @@ void ConfirmSubscriptionBox(
|
||||
}
|
||||
auto p = QPainter(userpic);
|
||||
p.drawImage(0, 0, state->frame);
|
||||
}, userpicWrap->lifetime());
|
||||
userpicWrap->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
}, userpic->lifetime());
|
||||
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
if (photo) {
|
||||
state->photoMedia = photo->createMediaView();
|
||||
state->photoMedia->wanted(Data::PhotoSize::Small, Data::FileOrigin());
|
||||
@@ -197,7 +196,7 @@ void ConfirmSubscriptionBox(
|
||||
session->downloaderTaskFinished(
|
||||
) | rpl::start_with_next([=] {
|
||||
userpic->update();
|
||||
}, userpicWrap->entity()->lifetime());
|
||||
}, userpic->lifetime());
|
||||
}
|
||||
} else {
|
||||
state->photoEmpty = std::make_unique<Ui::EmptyUserpic>(
|
||||
@@ -215,43 +214,40 @@ void ConfirmSubscriptionBox(
|
||||
2.);
|
||||
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_channel_invite_subscription_title(),
|
||||
st::inviteLinkSubscribeBoxTitle)));
|
||||
tr::lng_channel_invite_subscription_title(),
|
||||
st::inviteLinkSubscribeBoxTitle),
|
||||
style::al_top);
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_channel_invite_subscription_about(
|
||||
lt_channel,
|
||||
rpl::single(Ui::Text::Bold(name)),
|
||||
lt_price,
|
||||
tr::lng_credits_summary_options_credits(
|
||||
lt_count,
|
||||
rpl::single(amount) | tr::to_count(),
|
||||
Ui::Text::Bold),
|
||||
Ui::Text::WithEntities),
|
||||
st::inviteLinkSubscribeBoxAbout)));
|
||||
tr::lng_channel_invite_subscription_about(
|
||||
lt_channel,
|
||||
rpl::single(Ui::Text::Bold(name)),
|
||||
lt_price,
|
||||
tr::lng_credits_summary_options_credits(
|
||||
lt_count,
|
||||
rpl::single(amount) | tr::to_count(),
|
||||
Ui::Text::Bold),
|
||||
Ui::Text::WithEntities),
|
||||
st::inviteLinkSubscribeBoxAbout),
|
||||
style::al_top);
|
||||
Ui::AddSkip(content);
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_channel_invite_subscription_terms(
|
||||
lt_link,
|
||||
rpl::combine(
|
||||
tr::lng_paid_react_agree_link(),
|
||||
tr::lng_group_invite_subscription_about_url()
|
||||
) | rpl::map([](const QString &text, const QString &url) {
|
||||
return Ui::Text::Link(text, url);
|
||||
}),
|
||||
Ui::Text::RichLangValue),
|
||||
st::inviteLinkSubscribeBoxTerms)));
|
||||
tr::lng_channel_invite_subscription_terms(
|
||||
lt_link,
|
||||
rpl::combine(
|
||||
tr::lng_paid_react_agree_link(),
|
||||
tr::lng_group_invite_subscription_about_url()
|
||||
) | rpl::map([](const QString &text, const QString &url) {
|
||||
return Ui::Text::Link(text, url);
|
||||
}),
|
||||
Ui::Text::RichLangValue),
|
||||
st::inviteLinkSubscribeBoxTerms),
|
||||
style::al_top);
|
||||
|
||||
{
|
||||
const auto balance = Settings::AddBalanceWidget(
|
||||
|
||||
@@ -153,6 +153,7 @@ Data::CreditsHistoryEntry CreditsHistoryEntryFromTL(
|
||||
.floodSkip = int(tl.data().vfloodskip_number().value_or(0)),
|
||||
.converted = stargift && incoming,
|
||||
.stargift = stargift.has_value(),
|
||||
.postsSearch = tl.data().is_posts_search(),
|
||||
.giftUpgraded = tl.data().is_stargift_upgrade(),
|
||||
.giftResale = tl.data().is_stargift_resale(),
|
||||
.reaction = tl.data().is_reaction(),
|
||||
|
||||
@@ -45,15 +45,17 @@ namespace {
|
||||
auto options = PremiumSubscriptionOptionsFromTL(tlOptions);
|
||||
for (auto i = 0; i < options.size(); i++) {
|
||||
const auto &tlOption = tlOptions[i].data();
|
||||
const auto currency = qs(tlOption.vcurrency());
|
||||
const auto perUserText = Ui::FillAmountAndCurrency(
|
||||
tlOption.vamount().v / float64(tlOption.vusers().v),
|
||||
qs(tlOption.vcurrency()),
|
||||
currency,
|
||||
false);
|
||||
options[i].costPerMonth = perUserText
|
||||
+ ' '
|
||||
+ QChar(0x00D7)
|
||||
+ ' '
|
||||
+ QString::number(tlOption.vusers().v);
|
||||
options[i].currency = currency;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
@@ -613,24 +615,32 @@ std::vector<GiftOptionData> PremiumGiftCodeOptions::optionsForPeer() const {
|
||||
return result;
|
||||
}
|
||||
|
||||
Data::PremiumSubscriptionOptions PremiumGiftCodeOptions::options(int amount) {
|
||||
const auto it = _subscriptionOptions.find(amount);
|
||||
Data::PremiumSubscriptionOptions PremiumGiftCodeOptions::optionsForGiveaway(
|
||||
int usersCount) {
|
||||
const auto skipForStars = [&](Data::PremiumSubscriptionOptions options) {
|
||||
const auto proj = &Data::PremiumSubscriptionOption::currency;
|
||||
options.erase(
|
||||
ranges::remove(options, Ui::kCreditsCurrency, proj),
|
||||
end(options));
|
||||
return options;
|
||||
};
|
||||
const auto it = _subscriptionOptions.find(usersCount);
|
||||
if (it != end(_subscriptionOptions)) {
|
||||
return it->second;
|
||||
return skipForStars(it->second);
|
||||
} else {
|
||||
auto tlOptions = QVector<MTPPremiumGiftCodeOption>();
|
||||
for (auto i = 0; i < _optionsForOnePerson.months.size(); i++) {
|
||||
tlOptions.push_back(MTP_premiumGiftCodeOption(
|
||||
MTP_flags(MTPDpremiumGiftCodeOption::Flags(0)),
|
||||
MTP_int(amount),
|
||||
MTP_int(usersCount),
|
||||
MTP_int(_optionsForOnePerson.months[i]),
|
||||
MTPstring(),
|
||||
MTPint(),
|
||||
MTP_string(_optionsForOnePerson.currencies[i]),
|
||||
MTP_long(_optionsForOnePerson.totalCosts[i] * amount)));
|
||||
MTP_long(_optionsForOnePerson.totalCosts[i] * usersCount)));
|
||||
}
|
||||
_subscriptionOptions[amount] = GiftCodesFromTL(tlOptions);
|
||||
return _subscriptionOptions[amount];
|
||||
_subscriptionOptions[usersCount] = GiftCodesFromTL(tlOptions);
|
||||
return skipForStars(_subscriptionOptions[usersCount]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -950,12 +960,9 @@ std::optional<Data::SavedStarGift> FromTL(
|
||||
| ranges::to_vector)
|
||||
: std::vector<int>()),
|
||||
.message = (data.vmessage()
|
||||
? TextWithEntities{
|
||||
.text = qs(data.vmessage()->data().vtext()),
|
||||
.entities = Api::EntitiesFromMTP(
|
||||
session,
|
||||
data.vmessage()->data().ventities().v),
|
||||
}
|
||||
? Api::ParseTextWithEntities(
|
||||
session,
|
||||
*data.vmessage())
|
||||
: TextWithEntities()),
|
||||
.starsConverted = int64(data.vconvert_stars().value_or_empty()),
|
||||
.starsUpgradedBySender = int64(
|
||||
|
||||
@@ -180,7 +180,8 @@ public:
|
||||
|
||||
[[nodiscard]] rpl::producer<rpl::no_value, QString> request();
|
||||
[[nodiscard]] std::vector<GiftOptionData> optionsForPeer() const;
|
||||
[[nodiscard]] Data::PremiumSubscriptionOptions options(int amount);
|
||||
[[nodiscard]] Data::PremiumSubscriptionOptions optionsForGiveaway(
|
||||
int usersCount);
|
||||
[[nodiscard]] const std::vector<int> &availablePresets() const;
|
||||
[[nodiscard]] int monthsFromPreset(int monthsIndex);
|
||||
[[nodiscard]] Payments::InvoicePremiumGiftCode invoice(
|
||||
|
||||
@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_session_settings.h"
|
||||
|
||||
namespace Api {
|
||||
|
||||
@@ -25,6 +26,32 @@ Transcribes::Transcribes(not_null<ApiWrap*> api)
|
||||
, _api(&api->instance()) {
|
||||
}
|
||||
|
||||
bool Transcribes::isRated(not_null<HistoryItem*> item) const {
|
||||
const auto fullId = item->fullId();
|
||||
for (const auto &[transcribeId, id] : _ids) {
|
||||
if (id == fullId) {
|
||||
return _session->settings().isTranscriptionRated(transcribeId);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Transcribes::rate(not_null<HistoryItem*> item, bool isGood) {
|
||||
const auto fullId = item->fullId();
|
||||
for (const auto &[transcribeId, id] : _ids) {
|
||||
if (id == fullId) {
|
||||
_api.request(MTPmessages_RateTranscribedAudio(
|
||||
item->history()->peer->input,
|
||||
MTP_int(item->id),
|
||||
MTP_long(transcribeId),
|
||||
MTP_bool(isGood))).send();
|
||||
_session->settings().markTranscriptionAsRated(transcribeId);
|
||||
_session->saveSettings();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Transcribes::freeFor(not_null<HistoryItem*> item) const {
|
||||
if (const auto channel = item->history()->peer->asMegagroup()) {
|
||||
const auto owner = &channel->owner();
|
||||
|
||||
@@ -37,6 +37,8 @@ public:
|
||||
void apply(const MTPDupdateTranscribedAudio &update);
|
||||
|
||||
[[nodiscard]] bool freeFor(not_null<HistoryItem*> item) const;
|
||||
[[nodiscard]] bool isRated(not_null<HistoryItem*> item) const;
|
||||
void rate(not_null<HistoryItem*> item, bool isGood);
|
||||
|
||||
[[nodiscard]] bool trialsSupport();
|
||||
[[nodiscard]] TimeId trialsRefreshAt();
|
||||
|
||||
@@ -2458,7 +2458,16 @@ void ApiWrap::refreshFileReference(
|
||||
v::match(origin.data, [&](Data::FileOriginMessage data) {
|
||||
if (const auto item = _session->data().message(data)) {
|
||||
const auto media = item->media();
|
||||
const auto storyId = media ? media->storyId() : FullStoryId();
|
||||
const auto mediaStory = media ? media->storyId() : FullStoryId();
|
||||
const auto storyId = mediaStory
|
||||
? mediaStory
|
||||
: FullStoryId(
|
||||
(IsStoryMsgId(item->id)
|
||||
? item->history()->peer->id
|
||||
: PeerId()),
|
||||
(IsStoryMsgId(item->id)
|
||||
? StoryIdFromMsgId(item->id)
|
||||
: StoryId()));
|
||||
if (storyId) {
|
||||
request(MTPstories_GetStoriesByID(
|
||||
_session->data().peer(storyId.peer)->input,
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Data {
|
||||
struct UpdatedFileReferences;
|
||||
class WallPaper;
|
||||
struct ResolvedForwardDraft;
|
||||
enum class DefaultNotify;
|
||||
enum class DefaultNotify : uint8_t;
|
||||
enum class StickersType : uchar;
|
||||
class Forum;
|
||||
class ForumTopic;
|
||||
|
||||
@@ -13,12 +13,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/update_checker.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_channel_earn.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
#include "styles/style_premium.h"
|
||||
#include "styles/style_settings.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
@@ -160,3 +168,151 @@ QString currentVersionText() {
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
void ArchiveHintBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
bool unarchiveOnNewMessage,
|
||||
Fn<void()> onUnarchive) {
|
||||
box->setNoContentMargin(true);
|
||||
|
||||
const auto content = box->verticalLayout().get();
|
||||
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
{
|
||||
const auto &icon = st::dialogsArchiveUserpic;
|
||||
const auto rect = Rect(icon.size() * 2);
|
||||
auto owned = object_ptr<Ui::RpWidget>(content);
|
||||
owned->resize(rect.size());
|
||||
owned->setNaturalWidth(rect.width());
|
||||
const auto widget = box->addRow(std::move(owned), style::al_top);
|
||||
widget->paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
auto p = Painter(widget);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::activeButtonBg);
|
||||
p.drawEllipse(rect);
|
||||
icon.paintInCenter(p, rect);
|
||||
}, widget->lifetime());
|
||||
}
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
tr::lng_archive_hint_title(),
|
||||
st::boxTitle),
|
||||
style::al_top);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
{
|
||||
const auto label = box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
(unarchiveOnNewMessage
|
||||
? tr::lng_archive_hint_about_unmuted
|
||||
: tr::lng_archive_hint_about)(
|
||||
lt_link,
|
||||
tr::lng_archive_hint_about_link(
|
||||
lt_emoji,
|
||||
rpl::single(
|
||||
Ui::Text::IconEmoji(&st::textMoreIconEmoji)),
|
||||
Ui::Text::RichLangValue
|
||||
) | rpl::map([](TextWithEntities text) {
|
||||
return Ui::Text::Link(std::move(text), 1);
|
||||
}),
|
||||
Ui::Text::RichLangValue),
|
||||
st::channelEarnHistoryRecipientLabel));
|
||||
label->resizeToWidth(box->width()
|
||||
- rect::m::sum::h(st::boxRowPadding));
|
||||
label->setLink(
|
||||
1,
|
||||
std::make_shared<GenericClickHandler>([=](ClickContext context) {
|
||||
if (context.button == Qt::LeftButton) {
|
||||
onUnarchive();
|
||||
}
|
||||
}));
|
||||
}
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
{
|
||||
const auto padding = QMargins(
|
||||
st::settingsButton.padding.left(),
|
||||
st::boxRowPadding.top(),
|
||||
st::boxRowPadding.right(),
|
||||
st::boxRowPadding.bottom());
|
||||
const auto addEntry = [&](
|
||||
rpl::producer<QString> title,
|
||||
rpl::producer<QString> about,
|
||||
const style::icon &icon) {
|
||||
const auto top = content->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
std::move(title),
|
||||
st::channelEarnSemiboldLabel),
|
||||
padding);
|
||||
Ui::AddSkip(content, st::channelEarnHistoryThreeSkip);
|
||||
content->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
std::move(about),
|
||||
st::channelEarnHistoryRecipientLabel),
|
||||
padding);
|
||||
const auto left = Ui::CreateChild<Ui::RpWidget>(
|
||||
box->verticalLayout().get());
|
||||
left->paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
auto p = Painter(left);
|
||||
icon.paint(p, 0, 0, left->width());
|
||||
}, left->lifetime());
|
||||
left->resize(icon.size());
|
||||
top->geometryValue(
|
||||
) | rpl::start_with_next([=](const QRect &g) {
|
||||
left->moveToLeft(
|
||||
(g.left() - left->width()) / 2,
|
||||
g.top() + st::channelEarnHistoryThreeSkip);
|
||||
}, left->lifetime());
|
||||
};
|
||||
addEntry(
|
||||
tr::lng_archive_hint_section_1(),
|
||||
tr::lng_archive_hint_section_1_info(),
|
||||
st::menuIconArchive);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
addEntry(
|
||||
tr::lng_archive_hint_section_2(),
|
||||
tr::lng_archive_hint_section_2_info(),
|
||||
st::menuIconStealth);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
addEntry(
|
||||
tr::lng_archive_hint_section_3(),
|
||||
tr::lng_archive_hint_section_3_info(),
|
||||
st::menuIconStoriesSavedSection);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
}
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
{
|
||||
const auto &st = st::premiumPreviewDoubledLimitsBox;
|
||||
box->setStyle(st);
|
||||
auto button = object_ptr<Ui::RoundButton>(
|
||||
box,
|
||||
tr::lng_archive_hint_button(),
|
||||
st::defaultActiveButton);
|
||||
button->setTextTransform(
|
||||
Ui::RoundButton::TextTransform::NoTransform);
|
||||
button->resizeToWidth(box->width()
|
||||
- st.buttonPadding.left()
|
||||
- st.buttonPadding.left());
|
||||
button->setClickedCallback([=] { box->closeBox(); });
|
||||
box->addButton(std::move(button));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/layers/generic_box.h"
|
||||
|
||||
void AboutBox(not_null<Ui::GenericBox*> box);
|
||||
void ArchiveHintBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
bool unarchiveOnNewMessage,
|
||||
Fn<void()> onUnarchive);
|
||||
|
||||
QString telegramFaqLink();
|
||||
QString currentVersionText();
|
||||
|
||||
@@ -1116,8 +1116,16 @@ moderateBoxExpandInnerSkip: 2px;
|
||||
moderateBoxExpandFont: font(11px);
|
||||
moderateBoxExpandToggleSize: 4px;
|
||||
moderateBoxExpandToggleFourStrokes: 3px;
|
||||
moderateBoxExpandIcon: icon{{ "info/edit/expand_arrow_small-flip_vertical", windowActiveTextFg }};
|
||||
moderateBoxExpandIconDown: icon{{ "info/edit/expand_arrow_small", windowActiveTextFg }};
|
||||
moderateBoxExpandIcon: IconEmoji{
|
||||
icon: icon{{ "info/edit/expand_arrow_small-flip_vertical", windowActiveTextFg }};
|
||||
padding: margins(-2px, -1px, 0px, 0px);
|
||||
useIconColor: true;
|
||||
}
|
||||
moderateBoxExpandIconDown: IconEmoji{
|
||||
icon: icon{{ "info/edit/expand_arrow_small", windowActiveTextFg }};
|
||||
padding: margins(-2px, -1px, 0px, 0px);
|
||||
useIconColor: true;
|
||||
}
|
||||
moderateBoxDividerLabel: FlatLabel(boxDividerLabel) {
|
||||
palette: TextPalette(defaultTextPalette) {
|
||||
selectLinkFg: windowActiveTextFg;
|
||||
|
||||
@@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
@@ -111,16 +112,12 @@ void DeleteMessagesBox::prepare() {
|
||||
Ui::Text::RichLangValue);
|
||||
deleteStyle = &st::attentionBoxButton;
|
||||
} else if (_wipeHistoryJustClear) {
|
||||
const auto isChannel = peer->isBroadcast();
|
||||
const auto isPublicGroup = peer->isMegagroup()
|
||||
&& peer->asChannel()->isPublic();
|
||||
if (isChannel || isPublicGroup) {
|
||||
canDelete = false;
|
||||
}
|
||||
details.text = isChannel
|
||||
? tr::lng_no_clear_history_channel(tr::now)
|
||||
: isPublicGroup
|
||||
? tr::lng_no_clear_history_group(tr::now)
|
||||
_revokeJustClearForChannel = true;
|
||||
details.text = (peer->isChannel() && !peer->isMegagroup())
|
||||
? tr::lng_sure_delete_channel_history(
|
||||
tr::now,
|
||||
lt_channel,
|
||||
peer->name())
|
||||
: peer->isSelf()
|
||||
? tr::lng_sure_delete_saved_messages(tr::now)
|
||||
: peer->isUser()
|
||||
@@ -156,7 +153,8 @@ void DeleteMessagesBox::prepare() {
|
||||
}
|
||||
deleteStyle = &st::attentionBoxButton;
|
||||
}
|
||||
if (auto revoke = revokeText(peer)) {
|
||||
if (_revokeJustClearForChannel) {
|
||||
} else if (auto revoke = revokeText(peer)) {
|
||||
_revoke.create(
|
||||
this,
|
||||
revoke->checkbox,
|
||||
@@ -285,6 +283,7 @@ void DeleteMessagesBox::prepare() {
|
||||
}
|
||||
}
|
||||
_text.create(this, rpl::single(std::move(details)), st::boxLabel);
|
||||
_text->resizeToWidth(st::boxWidth - rect::m::sum::h(st::boxPadding));
|
||||
|
||||
if (_wipeHistoryJustClear && _wipeHistoryPeer) {
|
||||
const auto validator = TTLMenu::TTLValidator(
|
||||
@@ -554,7 +553,9 @@ void DeleteMessagesBox::deleteAndClear() {
|
||||
!_revoke->checked());
|
||||
Core::App().saveSettingsDelayed();
|
||||
}
|
||||
const auto revoke = _revoke ? _revoke->checked() : _revokeForBot;
|
||||
const auto revoke = _revoke
|
||||
? _revoke->checked()
|
||||
: (_revokeForBot || _revokeJustClearForChannel);
|
||||
const auto session = _session;
|
||||
const auto invokeCallbackAndClose = [&] {
|
||||
// deleteMessages can initiate closing of the current section,
|
||||
|
||||
@@ -75,6 +75,7 @@ private:
|
||||
bool _moderateDeleteAll = false;
|
||||
|
||||
bool _revokeForBot = false;
|
||||
bool _revokeJustClearForChannel = false;
|
||||
|
||||
object_ptr<Ui::FlatLabel> _text = { nullptr };
|
||||
object_ptr<Ui::Checkbox> _revoke = { nullptr };
|
||||
|
||||
@@ -1293,7 +1293,7 @@ void EditDirectMessagesPriceBox(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
box,
|
||||
object_ptr<Ui::VerticalLayout>(box)),
|
||||
{});
|
||||
style::margins());
|
||||
wrap->toggle(savedValue.has_value(), anim::type::instant);
|
||||
wrap->toggleOn(toggle->toggledChanges());
|
||||
|
||||
|
||||
@@ -538,25 +538,24 @@ void LinkController::addHeader(not_null<Ui::VerticalLayout*> container) {
|
||||
|
||||
const auto isStatic = _filterTitle.isStatic;
|
||||
verticalLayout->add(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
verticalLayout,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
verticalLayout,
|
||||
(_data.url.isEmpty()
|
||||
? tr::lng_filters_link_no_about(Ui::Text::WithEntities)
|
||||
: tr::lng_filters_link_share_about(
|
||||
lt_folder,
|
||||
rpl::single(Ui::Text::Wrapped(
|
||||
_filterTitle.text,
|
||||
EntityType::Bold)),
|
||||
Ui::Text::WithEntities)),
|
||||
st::settingsFilterDividerLabel,
|
||||
st::defaultPopupMenu,
|
||||
Core::TextContext({
|
||||
.session = &_window->session(),
|
||||
.customEmojiLoopLimit = isStatic ? -1 : 0,
|
||||
}))),
|
||||
st::filterLinkDividerLabelPadding);
|
||||
(_data.url.isEmpty()
|
||||
? tr::lng_filters_link_no_about(Ui::Text::WithEntities)
|
||||
: tr::lng_filters_link_share_about(
|
||||
lt_folder,
|
||||
rpl::single(Ui::Text::Wrapped(
|
||||
_filterTitle.text,
|
||||
EntityType::Bold)),
|
||||
Ui::Text::WithEntities)),
|
||||
st::settingsFilterDividerLabel,
|
||||
st::defaultPopupMenu,
|
||||
Core::TextContext({
|
||||
.session = &_window->session(),
|
||||
.customEmojiLoopLimit = isStatic ? -1 : 0,
|
||||
})),
|
||||
st::filterLinkDividerLabelPadding,
|
||||
style::al_top)->setTryMakeSimilarLines(true);
|
||||
|
||||
verticalLayout->geometryValue(
|
||||
) | rpl::start_with_next([=](const QRect &r) {
|
||||
|
||||
@@ -24,7 +24,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/rect.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/widgets/label_with_custom_emoji.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_channel_earn.h"
|
||||
@@ -53,9 +52,8 @@ void GiftCreditsBox(
|
||||
Ui::AddSkip(content);
|
||||
const auto &stUser = st::premiumGiftsUserpicButton;
|
||||
const auto userpicWrap = content->add(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
content,
|
||||
object_ptr<Ui::UserpicButton>(content, peer, stUser)));
|
||||
object_ptr<Ui::UserpicButton>(content, peer, stUser),
|
||||
style::al_top);
|
||||
userpicWrap->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
@@ -78,19 +76,17 @@ void GiftCreditsBox(
|
||||
u"internal:stars_examples"_q);
|
||||
});
|
||||
content->add(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
Ui::CreateLabelWithCustomEmoji(
|
||||
content,
|
||||
tr::lng_credits_box_history_entry_gift_out_about(
|
||||
lt_user,
|
||||
rpl::single(TextWithEntities{ peer->shortName() }),
|
||||
lt_link,
|
||||
std::move(link),
|
||||
Ui::Text::RichLangValue),
|
||||
Core::TextContext({ .session = &peer->session() }),
|
||||
st::creditsBoxAbout)),
|
||||
st::boxRowPadding);
|
||||
tr::lng_credits_box_history_entry_gift_out_about(
|
||||
lt_user,
|
||||
rpl::single(TextWithEntities{ peer->shortName() }),
|
||||
lt_link,
|
||||
std::move(link),
|
||||
Ui::Text::RichLangValue),
|
||||
st::creditsBoxAbout),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
}
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(box->verticalLayout());
|
||||
|
||||
@@ -466,8 +466,7 @@ void AddTableRow(
|
||||
}
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> MakeNonUniqueStatusTableValue(
|
||||
not_null<Ui::TableLayout*> table,
|
||||
Fn<void()> startUpgrade) {
|
||||
not_null<Ui::TableLayout*> table) {
|
||||
auto result = object_ptr<Ui::RpWidget>(table);
|
||||
const auto raw = result.data();
|
||||
|
||||
@@ -477,34 +476,10 @@ void AddTableRow(
|
||||
table->st().defaultValue,
|
||||
st::defaultPopupMenu);
|
||||
|
||||
const auto upgrade = startUpgrade
|
||||
? Ui::CreateChild<Ui::RoundButton>(
|
||||
raw,
|
||||
tr::lng_gift_unique_status_upgrade(),
|
||||
table->st().smallButton)
|
||||
: (Ui::RoundButton*)(nullptr);
|
||||
if (upgrade) {
|
||||
using namespace Ui;
|
||||
upgrade->setTextTransform(RoundButton::TextTransform::NoTransform);
|
||||
upgrade->setClickedCallback(startUpgrade);
|
||||
}
|
||||
|
||||
rpl::combine(
|
||||
raw->widthValue(),
|
||||
upgrade ? upgrade->widthValue() : rpl::single(0)
|
||||
) | rpl::start_with_next([=](int width, int toggleWidth) {
|
||||
const auto toggleSkip = toggleWidth
|
||||
? (st::normalFont->spacew + toggleWidth)
|
||||
: 0;
|
||||
label->resizeToNaturalWidth(width - toggleSkip);
|
||||
raw->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
label->resizeToNaturalWidth(width);
|
||||
label->moveToLeft(0, 0, width);
|
||||
if (upgrade) {
|
||||
upgrade->moveToLeft(
|
||||
label->width() + st::normalFont->spacew,
|
||||
(table->st().defaultValue.style.font->ascent
|
||||
- table->st().smallButton.style.font->ascent),
|
||||
width);
|
||||
}
|
||||
}, label->lifetime());
|
||||
|
||||
label->heightValue() | rpl::start_with_next([=](int height) {
|
||||
@@ -766,7 +741,8 @@ void GiftCodeBox(
|
||||
std::move(shareLink),
|
||||
Ui::Text::WithEntities)),
|
||||
st::giveawayGiftCodeFooter),
|
||||
st::giveawayGiftCodeFooterMargin);
|
||||
st::giveawayGiftCodeFooterMargin,
|
||||
style::al_top);
|
||||
footer->setClickHandlerFilter([=](const auto &...) {
|
||||
ShareWithFriend(controller, slug);
|
||||
return false;
|
||||
@@ -908,7 +884,8 @@ void GiftCodePendingBox(
|
||||
box,
|
||||
tr::lng_gift_link_pending_footer(),
|
||||
st::giveawayGiftCodeFooter),
|
||||
st::giveawayGiftCodeFooterMargin);
|
||||
st::giveawayGiftCodeFooterMargin,
|
||||
style::al_top);
|
||||
|
||||
const auto close = Ui::CreateChild<Ui::IconButton>(
|
||||
box.get(),
|
||||
@@ -1013,12 +990,11 @@ void GiveawayInfoBox(
|
||||
label->setTextColorOverride(st::windowActiveTextFg->c);
|
||||
}
|
||||
const auto result = box->addRow(
|
||||
object_ptr<Ui::PaddingWrap<Ui::CenterWrap<Ui::FlatLabel>>>(
|
||||
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>>(
|
||||
box.get(),
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
box.get(),
|
||||
std::move(label)),
|
||||
QMargins(0, skip, 0, skip)));
|
||||
std::move(label),
|
||||
QMargins(0, skip, 0, skip)),
|
||||
style::al_justify);
|
||||
result->paintRequest() | rpl::start_with_next([=] {
|
||||
auto p = QPainter(result);
|
||||
p.setPen(Qt::NoPen);
|
||||
@@ -1211,7 +1187,8 @@ void GiveawayInfoBox(
|
||||
: tr::lng_prizes_cancelled()),
|
||||
st::giveawayRefundedLabel),
|
||||
st::giveawayRefundedPadding),
|
||||
{ padding.left(), 0, padding.right(), padding.bottom() });
|
||||
{ padding.left(), 0, padding.right(), padding.bottom() },
|
||||
style::al_top);
|
||||
const auto bg = wrap->lifetime().make_state<Ui::RoundRect>(
|
||||
st::boxRadius,
|
||||
st::attentionBoxButton.textBgOver);
|
||||
@@ -1525,7 +1502,7 @@ void AddStarGiftTable(
|
||||
AddTableRow(
|
||||
table,
|
||||
tr::lng_gift_unique_status(),
|
||||
MakeNonUniqueStatusTableValue(table, std::move(startUpgrade)),
|
||||
MakeNonUniqueStatusTableValue(table),
|
||||
marginWithButton);
|
||||
}
|
||||
if (unique) {
|
||||
@@ -1674,7 +1651,8 @@ void AddCreditsHistoryEntryTable(
|
||||
show,
|
||||
peerId);
|
||||
}
|
||||
if (actorId || (!entry.starrefCommission && peerId)) {
|
||||
if (!entry.postsSearch
|
||||
&& (actorId || (!entry.starrefCommission && peerId))) {
|
||||
auto text = entry.starrefCommission
|
||||
? tr::lng_credits_box_history_entry_referred()
|
||||
: entry.in
|
||||
|
||||
@@ -403,22 +403,10 @@ void CreateModerateMessagesBox(
|
||||
const auto container = wrap->entity();
|
||||
wrap->toggle(false, anim::type::instant);
|
||||
|
||||
const auto session = &participants.front()->session();
|
||||
const auto emojiMargin = QMargins(
|
||||
-st::moderateBoxExpandInnerSkip,
|
||||
-st::moderateBoxExpandInnerSkip / 2,
|
||||
0,
|
||||
0);
|
||||
const auto emojiUp = Ui::Text::SingleCustomEmoji(
|
||||
session->data().customEmojiManager().registerInternalEmoji(
|
||||
st::moderateBoxExpandIcon,
|
||||
emojiMargin,
|
||||
false));
|
||||
const auto emojiDown = Ui::Text::SingleCustomEmoji(
|
||||
session->data().customEmojiManager().registerInternalEmoji(
|
||||
st::moderateBoxExpandIconDown,
|
||||
emojiMargin,
|
||||
false));
|
||||
const auto emojiUp = Ui::Text::IconEmoji(
|
||||
&st::moderateBoxExpandIcon);
|
||||
const auto emojiDown = Ui::Text::IconEmoji(
|
||||
&st::moderateBoxExpandIconDown);
|
||||
|
||||
auto label = object_ptr<Ui::FlatLabel>(
|
||||
inner,
|
||||
@@ -463,9 +451,7 @@ void CreateModerateMessagesBox(
|
||||
Ui::Text::WithEntities);
|
||||
}) | rpl::flatten_latest(
|
||||
) | rpl::start_with_next([=](const TextWithEntities &text) {
|
||||
raw->setMarkedText(
|
||||
Ui::Text::Link(text, u"internal:"_q),
|
||||
Core::TextContext({ .session = session }));
|
||||
raw->setMarkedText(Ui::Text::Link(text, u"internal:"_q));
|
||||
}, label->lifetime());
|
||||
|
||||
Ui::AddSkip(inner);
|
||||
|
||||
@@ -233,7 +233,8 @@ void FillUpgradeToPremiumCover(
|
||||
container,
|
||||
rpl::single(text),
|
||||
st::inviteForbiddenInfo),
|
||||
st::inviteForbiddenInfoPadding);
|
||||
st::inviteForbiddenInfoPadding,
|
||||
style::al_top);
|
||||
}
|
||||
|
||||
void SimpleForbiddenBox(
|
||||
@@ -511,7 +512,8 @@ void InviteForbiddenController::setComplexCover() {
|
||||
if (_can) {
|
||||
container->add(
|
||||
MakeShowOrLabel(container, tr::lng_invite_upgrade_or()),
|
||||
st::inviteForbiddenOrLabelPadding);
|
||||
st::inviteForbiddenOrLabelPadding,
|
||||
style::al_justify);
|
||||
}
|
||||
container->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
@@ -520,7 +522,8 @@ void InviteForbiddenController::setComplexCover() {
|
||||
? tr::lng_invite_upgrade_via_title()
|
||||
: tr::lng_via_link_cant()),
|
||||
st::inviteForbiddenTitle),
|
||||
st::inviteForbiddenTitlePadding);
|
||||
st::inviteForbiddenTitlePadding,
|
||||
style::al_top);
|
||||
|
||||
const auto about = _can
|
||||
? (_peer->isBroadcast()
|
||||
@@ -544,7 +547,8 @@ void InviteForbiddenController::setComplexCover() {
|
||||
container,
|
||||
rpl::single(about),
|
||||
st::inviteForbiddenInfo),
|
||||
st::inviteForbiddenInfoPadding);
|
||||
st::inviteForbiddenInfoPadding,
|
||||
style::al_top);
|
||||
}
|
||||
delegate()->peerListSetAboveWidget(std::move(cover));
|
||||
}
|
||||
|
||||
@@ -77,7 +77,9 @@ DefaultIconEmoji::DefaultIconEmoji(
|
||||
std::move(value) | rpl::start_with_next([=](DefaultIcon value) {
|
||||
_icon = value;
|
||||
_image = QImage();
|
||||
repaint();
|
||||
if (repaint) {
|
||||
repaint();
|
||||
}
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/background_box.h"
|
||||
#include "boxes/stickers_box.h"
|
||||
#include "chat_helpers/compose/compose_show.h"
|
||||
#include "core/ui_integration.h" // TextContext
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "data/data_changes.h"
|
||||
@@ -82,7 +81,6 @@ public:
|
||||
bool selected);
|
||||
|
||||
[[nodiscard]] uint8 index() const;
|
||||
int naturalWidth() const override;
|
||||
|
||||
void setSelected(bool selected);
|
||||
|
||||
@@ -164,7 +162,6 @@ private:
|
||||
void updateText();
|
||||
|
||||
const uint32 _level;
|
||||
const TextWithEntities _icon;
|
||||
const Ui::Text::MarkedContext _context;
|
||||
Ui::Text::String _text;
|
||||
bool _minimal = false;
|
||||
@@ -183,6 +180,15 @@ ColorSample::ColorSample(
|
||||
colorIndex
|
||||
) | rpl::start_with_next([=](uint8 index) {
|
||||
_index = index;
|
||||
setNaturalWidth([&] {
|
||||
if (_name.isEmpty() || _style->colorPatternIndex(_index)) {
|
||||
return st::settingsColorSampleSize;
|
||||
}
|
||||
const auto padding = st::settingsColorSamplePadding;
|
||||
return std::max(
|
||||
padding.left() + _name.maxWidth() + padding.right(),
|
||||
padding.top() + st::semiboldFont->height + padding.bottom());
|
||||
}());
|
||||
update();
|
||||
}, lifetime());
|
||||
}
|
||||
@@ -197,6 +203,7 @@ ColorSample::ColorSample(
|
||||
, _index(colorIndex)
|
||||
, _selected(selected)
|
||||
, _simple(true) {
|
||||
setNaturalWidth(st::settingsColorSampleSize);
|
||||
}
|
||||
|
||||
void ColorSample::setSelected(bool selected) {
|
||||
@@ -278,16 +285,6 @@ uint8 ColorSample::index() const {
|
||||
return _index;
|
||||
}
|
||||
|
||||
int ColorSample::naturalWidth() const {
|
||||
if (_name.isEmpty() || _style->colorPatternIndex(_index)) {
|
||||
return st::settingsColorSampleSize;
|
||||
}
|
||||
const auto padding = st::settingsColorSamplePadding;
|
||||
return std::max(
|
||||
padding.left() + _name.maxWidth() + padding.right(),
|
||||
padding.top() + st::semiboldFont->height + padding.bottom());
|
||||
}
|
||||
|
||||
PreviewWrap::PreviewWrap(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
std::shared_ptr<Ui::ChatStyle> style,
|
||||
@@ -460,22 +457,12 @@ LevelBadge::LevelBadge(
|
||||
uint32 level,
|
||||
not_null<Main::Session*> session)
|
||||
: Ui::RpWidget(parent)
|
||||
, _level(level)
|
||||
, _icon(Ui::Text::SingleCustomEmoji(
|
||||
session->data().customEmojiManager().registerInternalEmoji(
|
||||
st::settingsLevelBadgeLock,
|
||||
QMargins(0, st::settingsLevelBadgeLockSkip, 0, 0),
|
||||
false)))
|
||||
, _context(Core::TextContext({
|
||||
.session = session,
|
||||
.repaint = [this] { update(); },
|
||||
})) {
|
||||
, _level(level) {
|
||||
updateText();
|
||||
}
|
||||
|
||||
void LevelBadge::updateText() {
|
||||
auto text = _icon;
|
||||
text.append(' ');
|
||||
auto text = Ui::Text::IconEmoji(&st::settingsLevelBadgeLock).append(' ');
|
||||
if (!_minimal) {
|
||||
text.append(tr::lng_boost_level(
|
||||
tr::now,
|
||||
@@ -490,7 +477,7 @@ void LevelBadge::updateText() {
|
||||
st,
|
||||
text,
|
||||
kMarkupTextOptions,
|
||||
_context);
|
||||
Ui::Text::MarkedContext{ .repaint = [=] { update(); } });
|
||||
const auto &padding = st::settingsColorSamplePadding;
|
||||
QWidget::resize(
|
||||
_text.maxWidth() + rect::m::sum::h(padding),
|
||||
@@ -1213,7 +1200,7 @@ void EditPeerColorBox(
|
||||
peer,
|
||||
state->index.value(),
|
||||
state->emojiId.value()
|
||||
), {});
|
||||
), style::margins());
|
||||
|
||||
auto indices = peer->session().api().peerColors().suggestedValue();
|
||||
const auto margin = st::settingsColorRadioMargin;
|
||||
|
||||
@@ -1257,7 +1257,7 @@ void Controller::fillAutoTranslateButton() {
|
||||
_navigation->uiShow(),
|
||||
_peer,
|
||||
[=](int level) {
|
||||
if (const auto strong = weak.get()) {
|
||||
if (weak.get()) {
|
||||
state->isLocked = (level < requiredLevel);
|
||||
}
|
||||
return (level < requiredLevel)
|
||||
|
||||
@@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history.h"
|
||||
#include "history/history_item_helpers.h" // GetErrorForSending.
|
||||
#include "history/view/history_view_group_call_bar.h" // GenerateUserpics...
|
||||
#include "info/channel_statistics/earn/earn_icons.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "qr/qr_generate.h"
|
||||
@@ -42,6 +43,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/controls/userpic_button.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/text/custom_emoji_helper.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/toast/toast.h"
|
||||
@@ -247,6 +249,9 @@ private:
|
||||
const Role _role = Role::Joined;
|
||||
rpl::variable<LinkData> _data;
|
||||
|
||||
Ui::Text::CustomEmojiHelper _emojiHelper;
|
||||
TextWithEntities _creditsEmoji;
|
||||
|
||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||
rpl::event_stream<Processed> _processed;
|
||||
|
||||
@@ -408,6 +413,8 @@ Controller::Controller(
|
||||
const auto current = _data.current();
|
||||
_link = current.link;
|
||||
_revoked = current.revoked;
|
||||
_creditsEmoji = _emojiHelper.paletteDependent(
|
||||
Ui::Earn::IconCreditsEmoji());
|
||||
}
|
||||
|
||||
rpl::producer<LinkData> Controller::dataValue() const {
|
||||
@@ -725,7 +732,7 @@ void Controller::setupAboveJoinedWidget() {
|
||||
? tr::lng_group_invite_subscription_info_title(
|
||||
tr::now,
|
||||
lt_emoji,
|
||||
session().data().customEmojiManager().creditsEmoji(),
|
||||
_creditsEmoji,
|
||||
lt_price,
|
||||
{ QString::number(current.subscription.credits) },
|
||||
lt_multiplier,
|
||||
@@ -736,15 +743,12 @@ void Controller::setupAboveJoinedWidget() {
|
||||
: tr::lng_group_invite_subscription_info_title_none(
|
||||
tr::now,
|
||||
lt_emoji,
|
||||
session().data().customEmojiManager().creditsEmoji(),
|
||||
_creditsEmoji,
|
||||
lt_price,
|
||||
{ QString::number(current.subscription.credits) },
|
||||
Ui::Text::WithEntities),
|
||||
kMarkupTextOptions,
|
||||
Core::TextContext({
|
||||
.session = &session(),
|
||||
.repaint = [=] { widget->update(); },
|
||||
}));
|
||||
_emojiHelper.context([=] { widget->update(); }));
|
||||
auto &lifetime = widget->lifetime();
|
||||
const auto rateValue = lifetime.make_state<rpl::variable<float64>>(
|
||||
session().credits().rateValue(_peer));
|
||||
@@ -965,43 +969,41 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
|
||||
|
||||
const auto photoSize = st::boostReplaceUserpic.photoSize;
|
||||
const auto session = &row->peer()->session();
|
||||
content->add(object_ptr<Ui::CenterWrap<>>(
|
||||
content,
|
||||
Settings::SubscriptionUserpic(content, channel, photoSize)));
|
||||
content->add(
|
||||
Settings::SubscriptionUserpic(content, channel, photoSize),
|
||||
style::al_top);
|
||||
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
|
||||
box->addRow(object_ptr<Ui::CenterWrap<>>(
|
||||
box,
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_credits_box_subscription_title(),
|
||||
st::creditsBoxAboutTitle)));
|
||||
st::creditsBoxAboutTitle),
|
||||
style::al_top);
|
||||
|
||||
Ui::AddSkip(content);
|
||||
|
||||
const auto subtitle1 = box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
st::creditsTopupPrice)))->entity();
|
||||
st::creditsTopupPrice),
|
||||
style::al_top);
|
||||
subtitle1->setMarkedText(
|
||||
tr::lng_credits_subscription_subtitle(
|
||||
tr::now,
|
||||
lt_emoji,
|
||||
session->data().customEmojiManager().creditsEmoji(),
|
||||
_creditsEmoji,
|
||||
lt_cost,
|
||||
{ QString::number(data.subscription.credits) },
|
||||
Ui::Text::WithEntities),
|
||||
Core::TextContext({ .session = session }));
|
||||
_emojiHelper.context());
|
||||
const auto subtitle2 = box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
st::creditsTopupPrice)))->entity();
|
||||
st::creditsTopupPrice),
|
||||
style::al_top);
|
||||
session->credits().rateValue(
|
||||
channel
|
||||
) | rpl::start_with_next([=, currency = u"USD"_q](float64 rate) {
|
||||
@@ -1023,8 +1025,7 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
|
||||
box->addRow(object_ptr<Ui::CenterWrap<>>(
|
||||
box,
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_credits_box_out_about(
|
||||
@@ -1033,7 +1034,8 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
|
||||
) | Ui::Text::ToLink(
|
||||
tr::lng_credits_box_out_about_link(tr::now)),
|
||||
Ui::Text::WithEntities),
|
||||
st::creditsBoxAboutDivider)));
|
||||
st::creditsBoxAboutDivider),
|
||||
style::al_top);
|
||||
|
||||
const auto button = box->addButton(tr::lng_box_ok(), [=] {
|
||||
box->closeBox();
|
||||
|
||||
@@ -886,32 +886,18 @@ rpl::producer<int> AddSlowmodeSlider(
|
||||
return secondsCount->value();
|
||||
}
|
||||
|
||||
void AddBoostsUnrestrictLabels(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<Main::Session*> session) {
|
||||
void AddBoostsUnrestrictLabels(not_null<Ui::VerticalLayout*> container) {
|
||||
const auto labels = container->add(
|
||||
object_ptr<Ui::FixedHeightWidget>(container, st::normalFont->height),
|
||||
st::slowmodeLabelsMargin);
|
||||
const auto manager = &session->data().customEmojiManager();
|
||||
const auto one = Ui::Text::SingleCustomEmoji(
|
||||
manager->registerInternalEmoji(
|
||||
st::boostMessageIcon,
|
||||
st::boostMessageIconPadding));
|
||||
const auto many = Ui::Text::SingleCustomEmoji(
|
||||
manager->registerInternalEmoji(
|
||||
st::boostsMessageIcon,
|
||||
st::boostsMessageIconPadding));
|
||||
const auto context = Core::TextContext({
|
||||
.session = session,
|
||||
.customEmojiLoopLimit = 1,
|
||||
});
|
||||
const auto one = Ui::Text::IconEmoji(&st::boostMessageIcon);
|
||||
const auto many = Ui::Text::IconEmoji(&st::boostsMessageIcon);
|
||||
for (auto i = 0; i != kBoostsUnrestrictValues; ++i) {
|
||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||
labels,
|
||||
st::boostsUnrestrictLabel);
|
||||
label->setMarkedText(
|
||||
TextWithEntities(i ? many : one).append(QString::number(i + 1)),
|
||||
context);
|
||||
TextWithEntities(i ? many : one).append(QString::number(i + 1)));
|
||||
rpl::combine(
|
||||
labels->widthValue(),
|
||||
label->widthValue()
|
||||
@@ -977,7 +963,7 @@ rpl::producer<int> AddBoostsUnrestrictSlider(
|
||||
|
||||
const auto inner = outer->entity();
|
||||
|
||||
AddBoostsUnrestrictLabels(inner, &peer->session());
|
||||
AddBoostsUnrestrictLabels(inner);
|
||||
|
||||
const auto slider = inner->add(
|
||||
object_ptr<Ui::MediaSlider>(inner, st::localStorageLimitSlider),
|
||||
|
||||
@@ -221,7 +221,8 @@ void Controller::prepare() {
|
||||
Ui::Text::RichLangValue),
|
||||
Ui::Text::RichLangValue),
|
||||
st::boostReassignText),
|
||||
st::boxRowPadding);
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
delegate()->peerListSetAboveWidget(std::move(above));
|
||||
|
||||
const auto now = base::unixtime::now();
|
||||
|
||||
@@ -585,14 +585,14 @@ void ChannelsLimitBox(
|
||||
|
||||
const auto content = box->addRow(
|
||||
object_ptr<PeerListContent>(box, controller),
|
||||
{});
|
||||
style::margins());
|
||||
delegate->setContent(content);
|
||||
controller->setDelegate(delegate);
|
||||
|
||||
const auto count = 100;
|
||||
const auto placeholder = box->addRow(
|
||||
object_ptr<PeerListDummy>(box, count, st::defaultPeerList),
|
||||
{});
|
||||
style::margins());
|
||||
|
||||
using namespace rpl::mappers;
|
||||
controller->countValue(
|
||||
@@ -676,14 +676,14 @@ void PublicLinksLimitBox(
|
||||
|
||||
const auto content = box->addRow(
|
||||
object_ptr<PeerListContent>(box, controller),
|
||||
{});
|
||||
style::margins());
|
||||
delegate->setContent(content);
|
||||
controller->setDelegate(delegate);
|
||||
|
||||
const auto count = defaultLimit;
|
||||
const auto placeholder = box->addRow(
|
||||
object_ptr<PeerListDummy>(box, count, st::defaultPeerList),
|
||||
{});
|
||||
style::margins());
|
||||
|
||||
using namespace rpl::mappers;
|
||||
controller->countValue(
|
||||
|
||||
@@ -902,7 +902,7 @@ void PreviewBox(
|
||||
|
||||
const auto outer = box->addRow(
|
||||
ChatBackPreview(box, size.height(), back),
|
||||
{});
|
||||
style::margins());
|
||||
|
||||
struct Hiding {
|
||||
not_null<Ui::RpWidget*> widget;
|
||||
@@ -1079,26 +1079,21 @@ void PreviewBox(
|
||||
auto text = state->selected.value(
|
||||
) | rpl::map(SectionAbout) | rpl::flatten_latest();
|
||||
|
||||
const auto padding = st::premiumPreviewAboutPadding;
|
||||
const auto available = size.width() - padding.left() - padding.right();
|
||||
auto titleLabel = object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
std::move(title),
|
||||
st::premiumPreviewAboutTitle);
|
||||
titleLabel->resizeToWidth(available);
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
std::move(titleLabel)),
|
||||
st::premiumPreviewAboutTitlePadding);
|
||||
auto textLabel = object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
std::move(text),
|
||||
st::premiumPreviewAbout);
|
||||
textLabel->resizeToWidth(available);
|
||||
std::move(title),
|
||||
st::premiumPreviewAboutTitle),
|
||||
st::premiumPreviewAboutTitlePadding,
|
||||
style::al_top);
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(box, std::move(textLabel)),
|
||||
padding);
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
std::move(text),
|
||||
st::premiumPreviewAbout),
|
||||
st::premiumPreviewAboutPadding,
|
||||
style::al_top
|
||||
)->setTryMakeSimilarLines(true);
|
||||
box->addRow(
|
||||
CreateSwitch(box->verticalLayout(), &state->selected, state->order),
|
||||
st::premiumDotsMargin);
|
||||
|
||||
@@ -7,8 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "boxes/ringtones_box.h"
|
||||
|
||||
#include "data/notify/data_peer_notify_volume.h"
|
||||
#include "data/notify/data_peer_notify_settings.h"
|
||||
#include "api/api_ringtones.h"
|
||||
#include "apiwrap.h"
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
#include "base/call_delayed.h"
|
||||
#include "base/event_filter.h"
|
||||
#include "base/timer_rpl.h"
|
||||
@@ -21,11 +24,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_document_media.h"
|
||||
#include "data/data_document_resolver.h"
|
||||
#include "data/data_thread.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/notify/data_notify_settings.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_session_settings.h"
|
||||
#include "media/audio/media_audio.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "platform/platform_notifications_manager.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
@@ -111,7 +117,8 @@ void RingtonesBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Main::Session*> session,
|
||||
Data::NotifySound selected,
|
||||
Fn<void(Data::NotifySound)> save) {
|
||||
Fn<void(Data::NotifySound)> save,
|
||||
Data::VolumeController volumeController) {
|
||||
box->setTitle(tr::lng_ringtones_box_title());
|
||||
|
||||
const auto container = box->verticalLayout();
|
||||
@@ -128,12 +135,17 @@ void RingtonesBox(
|
||||
QPointer<Ui::Radiobutton> defaultButton;
|
||||
QPointer<Ui::Radiobutton> chosenButton;
|
||||
std::vector<QPointer<Ui::Radiobutton>> buttons;
|
||||
ushort presavedVolume = 0;
|
||||
};
|
||||
const auto state = container->lifetime().make_state<State>(State{
|
||||
.group = std::make_shared<Ui::RadiobuttonGroup>(),
|
||||
.chosen = selected,
|
||||
});
|
||||
|
||||
const auto volumeOverride = [volume = volumeController.volume] {
|
||||
return volume ? (0.01 * volume()) : -1;
|
||||
};
|
||||
|
||||
const auto addToGroup = [=](
|
||||
not_null<Ui::VerticalLayout*> verticalLayout,
|
||||
int value,
|
||||
@@ -156,7 +168,10 @@ void RingtonesBox(
|
||||
if (value == kDefaultValue) {
|
||||
state->defaultButton = button;
|
||||
button->setClickedCallback([=] {
|
||||
Core::App().notifications().playSound(session, 0);
|
||||
Core::App().notifications().playSound(
|
||||
session,
|
||||
0,
|
||||
volumeOverride());
|
||||
});
|
||||
}
|
||||
if (value < 0) {
|
||||
@@ -170,7 +185,8 @@ void RingtonesBox(
|
||||
if (media->loaded()) {
|
||||
Core::App().notifications().playSound(
|
||||
session,
|
||||
media->owner()->id);
|
||||
media->owner()->id,
|
||||
volumeOverride());
|
||||
}
|
||||
});
|
||||
base::install_event_filter(button, [=](not_null<QEvent*> e) {
|
||||
@@ -320,6 +336,31 @@ void RingtonesBox(
|
||||
}));
|
||||
});
|
||||
|
||||
if (volumeController.volume && volumeController.saveVolume) {
|
||||
auto saveAndTestVolume = [=](ushort currentVolume) {
|
||||
state->presavedVolume = currentVolume;
|
||||
const auto value = state->group->current();
|
||||
if (value != kNoSoundValue) {
|
||||
Core::App().notifications().playSound(
|
||||
session,
|
||||
(value == kDefaultValue)
|
||||
? 0
|
||||
: state->medias[value]->owner()->id,
|
||||
0.01 * currentVolume);
|
||||
}
|
||||
};
|
||||
Ui::AddRingtonesVolumeSlider(
|
||||
container,
|
||||
state->group->value() | rpl::map([=](int value) {
|
||||
return value != kNoSoundValue;
|
||||
}),
|
||||
tr::lng_ringtones_box_volume(),
|
||||
Data::VolumeController{
|
||||
base::take(volumeController.volume),
|
||||
std::move(saveAndTestVolume),
|
||||
});
|
||||
}
|
||||
|
||||
box->addSkip(st::ringtonesBoxSkip);
|
||||
Ui::AddDividerText(container, tr::lng_ringtones_box_about());
|
||||
|
||||
@@ -333,6 +374,9 @@ void RingtonesBox(
|
||||
: (value == kNoSoundValue)
|
||||
? Data::NotifySound{ .none = true }
|
||||
: Data::NotifySound{ .id = state->medias[value]->owner()->id };
|
||||
if (state->presavedVolume) {
|
||||
volumeController.saveVolume(state->presavedVolume);
|
||||
}
|
||||
save(sound);
|
||||
box->closeBox();
|
||||
});
|
||||
@@ -345,5 +389,5 @@ void ThreadRingtonesBox(
|
||||
const auto now = thread->owner().notifySettings().sound(thread);
|
||||
RingtonesBox(box, &thread->session(), now, [=](Data::NotifySound sound) {
|
||||
thread->owner().notifySettings().update(thread, {}, {}, sound);
|
||||
});
|
||||
}, Data::ThreadRingtonesVolumeController(thread));
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace Data {
|
||||
struct NotifySound;
|
||||
class Thread;
|
||||
enum class DefaultNotify : uint8_t;
|
||||
struct VolumeController;
|
||||
} // namespace Data
|
||||
|
||||
namespace Main {
|
||||
@@ -28,7 +30,8 @@ void RingtonesBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Main::Session*> session,
|
||||
Data::NotifySound selected,
|
||||
Fn<void(Data::NotifySound)> save);
|
||||
Fn<void(Data::NotifySound)> save,
|
||||
Data::VolumeController volumeController);
|
||||
|
||||
void ThreadRingtonesBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
|
||||
@@ -348,9 +348,9 @@ void SendCreditsBox(
|
||||
}, ministarsContainer->lifetime());
|
||||
}
|
||||
|
||||
const auto thumb = box->addRow(object_ptr<Ui::CenterWrap<>>(
|
||||
content,
|
||||
SendCreditsThumbnail(content, session, form.get(), photoSize)));
|
||||
const auto thumb = box->addRow(
|
||||
SendCreditsThumbnail(content, session, form.get(), photoSize),
|
||||
style::al_top);
|
||||
thumb->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
if (form->invoice.subscriptionPeriod) {
|
||||
const auto badge = SendCreditsBadge(content, form->invoice.amount);
|
||||
@@ -364,31 +364,30 @@ void SendCreditsBox(
|
||||
}
|
||||
|
||||
Ui::AddSkip(content);
|
||||
box->addRow(object_ptr<Ui::CenterWrap<>>(
|
||||
box,
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
form->invoice.subscriptionPeriod
|
||||
? rpl::single(form->title)
|
||||
: tr::lng_credits_box_out_title(),
|
||||
st::settingsPremiumUserTitle)));
|
||||
st::settingsPremiumUserTitle),
|
||||
style::al_top);
|
||||
if (form->invoice.subscriptionPeriod && form->botId && form->photo) {
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
const auto bot = session->data().user(form->botId);
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
box,
|
||||
Ui::CreatePeerBubble(box, bot)));
|
||||
Ui::CreatePeerBubble(box, bot),
|
||||
style::al_top);
|
||||
Ui::AddSkip(content);
|
||||
}
|
||||
Ui::AddSkip(content);
|
||||
box->addRow(object_ptr<Ui::CenterWrap<>>(
|
||||
box,
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
SendCreditsConfirmText(session, form.get()),
|
||||
st::creditsBoxAbout)));
|
||||
st::creditsBoxAbout),
|
||||
style::al_top);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
|
||||
@@ -455,7 +454,7 @@ void SendCreditsBox(
|
||||
lt_count,
|
||||
rpl::single(form->invoice.amount) | tr::to_count(),
|
||||
lt_emoji,
|
||||
rpl::single(CreditsEmojiSmall(session)),
|
||||
rpl::single(CreditsEmojiSmall()),
|
||||
Ui::Text::RichLangValue),
|
||||
state->confirmButtonBusy.value()
|
||||
) | rpl::map([](TextWithEntities &&text, bool busy) {
|
||||
@@ -502,16 +501,13 @@ void SendCreditsBox(
|
||||
}
|
||||
}
|
||||
|
||||
TextWithEntities CreditsEmoji(not_null<Main::Session*> session) {
|
||||
return Ui::Text::SingleCustomEmoji(
|
||||
session->data().customEmojiManager().registerInternalEmoji(
|
||||
st::settingsPremiumIconStar,
|
||||
QMargins{ 0, -st::moderateBoxExpandInnerSkip, 0, 0 },
|
||||
true),
|
||||
TextWithEntities CreditsEmoji() {
|
||||
return Ui::Text::IconEmoji(
|
||||
&st::starIconEmojiLarge,
|
||||
QString(QChar(0x2B50)));
|
||||
}
|
||||
|
||||
TextWithEntities CreditsEmojiSmall(not_null<Main::Session*> session) {
|
||||
TextWithEntities CreditsEmojiSmall() {
|
||||
return Ui::Text::IconEmoji(
|
||||
&st::starIconEmoji,
|
||||
QString(QChar(0x2B50)));
|
||||
|
||||
@@ -32,11 +32,9 @@ void SendCreditsBox(
|
||||
std::shared_ptr<Payments::CreditsFormData> data,
|
||||
Fn<void()> sent);
|
||||
|
||||
[[nodiscard]] TextWithEntities CreditsEmoji(
|
||||
not_null<Main::Session*> session);
|
||||
[[nodiscard]] TextWithEntities CreditsEmoji();
|
||||
|
||||
[[nodiscard]] TextWithEntities CreditsEmojiSmall(
|
||||
not_null<Main::Session*> session);
|
||||
[[nodiscard]] TextWithEntities CreditsEmojiSmall();
|
||||
|
||||
not_null<FlatLabel*> SetButtonMarkedLabel(
|
||||
not_null<RpWidget*> button,
|
||||
|
||||
@@ -843,9 +843,8 @@ void SendFilesBox::refreshPriceTag() {
|
||||
QPainter(raw).drawImage(0, 0, _priceTagBg);
|
||||
}, raw->lifetime());
|
||||
|
||||
const auto session = &_show->session();
|
||||
auto price = _price.value() | rpl::map([=](uint64 amount) {
|
||||
auto result = Ui::Text::Colorized(Ui::CreditsEmoji(session));
|
||||
auto result = Ui::Text::Colorized(Ui::CreditsEmoji());
|
||||
result.append(Lang::FormatCountDecimal(amount));
|
||||
return result;
|
||||
});
|
||||
@@ -857,10 +856,10 @@ void SendFilesBox::refreshPriceTag() {
|
||||
raw,
|
||||
QString(),
|
||||
st::paidTagLabel);
|
||||
std::move(text) | rpl::start_with_next([=](TextWithEntities &&text) {
|
||||
label->setMarkedText(text, Core::TextContext({
|
||||
.session = session,
|
||||
}));
|
||||
std::move(
|
||||
text
|
||||
) | rpl::start_with_next([=](const TextWithEntities &text) {
|
||||
label->setMarkedText(text);
|
||||
}, label->lifetime());
|
||||
label->show();
|
||||
label->sizeValue() | rpl::start_with_next([=](QSize size) {
|
||||
|
||||
@@ -54,6 +54,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "info/channel_statistics/earn/earn_icons.h"
|
||||
#include "info/peer_gifts/info_peer_gifts_common.h"
|
||||
#include "info/profile/info_profile_icon.h"
|
||||
#include "lang/lang_keys.h"
|
||||
@@ -82,6 +83,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/new_badges.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/text/custom_emoji_helper.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/toast/toast.h"
|
||||
@@ -1194,8 +1196,9 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
|
||||
[[nodiscard]] Text::String TabTextForPrice(
|
||||
not_null<Main::Session*> session,
|
||||
int price) {
|
||||
int price,
|
||||
TextWithEntities creditsIcon,
|
||||
Ui::Text::MarkedContext context) {
|
||||
const auto simple = [](const QString &text) {
|
||||
return Text::String(st::semiboldTextStyle, text);
|
||||
};
|
||||
@@ -1210,13 +1213,12 @@ void PreviewWrap::paintEvent(QPaintEvent *e) {
|
||||
} else if (price == kPriceTabResale) {
|
||||
return simple(tr::lng_gift_stars_tabs_resale(tr::now));
|
||||
}
|
||||
auto &manager = session->data().customEmojiManager();
|
||||
auto result = Text::String();
|
||||
result.setMarkedText(
|
||||
st::semiboldTextStyle,
|
||||
manager.creditsEmoji().append(QString::number(price)),
|
||||
creditsIcon.append(QString::number(price)),
|
||||
kMarkupTextOptions,
|
||||
Core::TextContext({ .session = session }));
|
||||
context);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1636,7 +1638,6 @@ struct GiftPriceTabs {
|
||||
object_ptr<RpWidget> widget;
|
||||
};
|
||||
[[nodiscard]] GiftPriceTabs MakeGiftsPriceTabs(
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<PeerData*> peer,
|
||||
rpl::producer<std::vector<GiftTypeStars>> gifts,
|
||||
bool hasMyUnique) {
|
||||
@@ -1745,7 +1746,6 @@ struct GiftPriceTabs {
|
||||
state->priceTab = state->buttons[index].price;
|
||||
};
|
||||
|
||||
const auto session = &peer->session();
|
||||
state->prices.value(
|
||||
) | rpl::start_with_next([=](const std::vector<int> &prices) {
|
||||
auto x = st::giftBoxTabsMargin.left();
|
||||
@@ -1759,12 +1759,18 @@ struct GiftPriceTabs {
|
||||
currentPrice = kPriceTabAll;
|
||||
}
|
||||
state->active = -1;
|
||||
auto helper = Ui::Text::CustomEmojiHelper();
|
||||
const auto creditsIcon = helper.paletteDependent(
|
||||
Ui::Earn::IconCreditsEmoji());
|
||||
for (auto i = 0, count = int(prices.size()); i != count; ++i) {
|
||||
const auto price = prices[i];
|
||||
auto &button = state->buttons[i];
|
||||
if (button.text.isEmpty() || button.price != price) {
|
||||
button.price = price;
|
||||
button.text = TabTextForPrice(session, price);
|
||||
button.text = TabTextForPrice(
|
||||
price,
|
||||
creditsIcon,
|
||||
helper.context());
|
||||
}
|
||||
button.active = (price == currentPrice);
|
||||
if (button.active) {
|
||||
@@ -2177,7 +2183,6 @@ void SoldOutBox(
|
||||
|
||||
void AddUpgradeButton(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<Main::Session*> session,
|
||||
int cost,
|
||||
not_null<PeerData*> peer,
|
||||
Fn<void(bool)> toggled,
|
||||
@@ -2190,7 +2195,8 @@ void AddUpgradeButton(
|
||||
button->toggleOn(rpl::single(false))->toggledValue(
|
||||
) | rpl::start_with_next(toggled, button->lifetime());
|
||||
|
||||
auto star = session->data().customEmojiManager().creditsEmoji();
|
||||
auto helper = Ui::Text::CustomEmojiHelper();
|
||||
auto star = helper.paletteDependent(Ui::Earn::IconCreditsEmoji());
|
||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||
button,
|
||||
tr::lng_gift_send_unique(
|
||||
@@ -2201,7 +2207,7 @@ void AddUpgradeButton(
|
||||
Text::WithEntities),
|
||||
st::boxLabel,
|
||||
st::defaultPopupMenu,
|
||||
Core::TextContext({ .session = session }));
|
||||
helper.context());
|
||||
label->show();
|
||||
label->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
button->widthValue() | rpl::start_with_next([=](int outer) {
|
||||
@@ -2355,7 +2361,7 @@ void SendGiftBox(
|
||||
});
|
||||
|
||||
auto cost = state->details.value(
|
||||
) | rpl::map([session](const GiftDetails &details) {
|
||||
) | rpl::map([](const GiftDetails &details) {
|
||||
return v::match(details.descriptor, [&](const GiftTypePremium &data) {
|
||||
const auto stars = (details.byStars && data.stars)
|
||||
? data.stars
|
||||
@@ -2363,7 +2369,7 @@ void SendGiftBox(
|
||||
? data.cost
|
||||
: 0;
|
||||
if (stars) {
|
||||
return CreditsEmojiSmall(session).append(
|
||||
return CreditsEmojiSmall().append(
|
||||
Lang::FormatCountDecimal(std::abs(stars)));
|
||||
}
|
||||
return TextWithEntities{
|
||||
@@ -2372,7 +2378,7 @@ void SendGiftBox(
|
||||
}, [&](const GiftTypeStars &data) {
|
||||
const auto amount = std::abs(data.info.stars)
|
||||
+ (details.upgraded ? data.info.starsToUpgrade : 0);
|
||||
return CreditsEmojiSmall(session).append(
|
||||
return CreditsEmojiSmall().append(
|
||||
Lang::FormatCountDecimal(amount));
|
||||
});
|
||||
});
|
||||
@@ -2448,8 +2454,7 @@ void SendGiftBox(
|
||||
const auto showing = std::make_shared<bool>();
|
||||
AddDivider(container);
|
||||
AddSkip(container);
|
||||
AddUpgradeButton(container, session, costToUpgrade, peer, [=](
|
||||
bool on) {
|
||||
AddUpgradeButton(container, costToUpgrade, peer, [=](bool on) {
|
||||
auto now = state->details.current();
|
||||
now.upgraded = on;
|
||||
state->details = std::move(now);
|
||||
@@ -2976,13 +2981,15 @@ void AddBlock(
|
||||
content,
|
||||
std::move(args.subtitle),
|
||||
st::giftBoxSubtitle),
|
||||
st::giftBoxSubtitleMargin);
|
||||
st::giftBoxSubtitleMargin,
|
||||
style::al_top);
|
||||
const auto about = content->add(
|
||||
object_ptr<FlatLabel>(
|
||||
content,
|
||||
std::move(args.about),
|
||||
st::giftBoxAbout),
|
||||
st::giftBoxAboutMargin);
|
||||
st::giftBoxAboutMargin,
|
||||
style::al_top);
|
||||
about->setClickHandlerFilter(std::move(args.aboutFilter));
|
||||
content->add(std::move(args.content));
|
||||
}
|
||||
@@ -3027,7 +3034,6 @@ void AddBlock(
|
||||
state->gifts = GiftsStars(&window->session(), peer);
|
||||
|
||||
auto tabs = MakeGiftsPriceTabs(
|
||||
window,
|
||||
peer,
|
||||
state->gifts.value(),
|
||||
!state->my.list.empty() && !peer->isSelf());
|
||||
@@ -3145,10 +3151,9 @@ void GiftBox(
|
||||
&& uniqueDisallowed;
|
||||
|
||||
content->add(
|
||||
object_ptr<CenterWrap<UserpicButton>>(
|
||||
content,
|
||||
object_ptr<UserpicButton>(content, peer, stUser))
|
||||
)->entity()->setClickedCallback([=] { window->showPeerInfo(peer); });
|
||||
object_ptr<UserpicButton>(content, peer, stUser),
|
||||
style::al_top
|
||||
)->setClickedCallback([=] { window->showPeerInfo(peer); });
|
||||
AddSkip(content);
|
||||
AddSkip(content);
|
||||
|
||||
@@ -4377,13 +4382,15 @@ void ShowUniqueGiftWearBox(
|
||||
lt_name,
|
||||
rpl::single(UniqueGiftName(gift))),
|
||||
st.title ? *st.title : st::uniqueGiftTitle),
|
||||
st::settingsPremiumRowTitlePadding);
|
||||
st::settingsPremiumRowTitlePadding,
|
||||
style::al_top);
|
||||
content->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
tr::lng_gift_wear_about(),
|
||||
st.subtitle ? *st.subtitle : st::uniqueGiftSubtitle),
|
||||
st::settingsPremiumRowAboutPadding);
|
||||
st::settingsPremiumRowAboutPadding,
|
||||
style::al_top);
|
||||
infoRow(
|
||||
tr::lng_gift_wear_badge_title(),
|
||||
(channel
|
||||
@@ -4445,11 +4452,7 @@ void ShowUniqueGiftWearBox(
|
||||
u"wear_collectibles"_q);
|
||||
}
|
||||
});
|
||||
const auto lock = Ui::Text::SingleCustomEmoji(
|
||||
session->data().customEmojiManager().registerInternalEmoji(
|
||||
st::historySendDisabledIcon,
|
||||
st::giftBoxLockMargins,
|
||||
true));
|
||||
const auto lock = Ui::Text::IconEmoji(&st::giftBoxLock);
|
||||
auto label = rpl::combine(
|
||||
tr::lng_gift_wear_start(),
|
||||
Data::AmPremiumValue(&show->session())
|
||||
@@ -4915,19 +4918,18 @@ void UpgradeBox(
|
||||
object_ptr<PlainShadow>(container),
|
||||
st::boxRowPadding + QMargins(0, skip, 0, skip));
|
||||
const auto checkbox = container->add(
|
||||
object_ptr<CenterWrap<Checkbox>>(
|
||||
object_ptr<Checkbox>(
|
||||
container,
|
||||
object_ptr<Checkbox>(
|
||||
container,
|
||||
(args.canAddComment
|
||||
? tr::lng_gift_upgrade_add_comment(tr::now)
|
||||
: args.canAddSender
|
||||
? tr::lng_gift_upgrade_add_sender(tr::now)
|
||||
: args.canAddMyComment
|
||||
? tr::lng_gift_upgrade_add_my_comment(tr::now)
|
||||
: tr::lng_gift_upgrade_add_my(tr::now)),
|
||||
args.addDetailsDefault)),
|
||||
st::defaultCheckbox.margin)->entity();
|
||||
(args.canAddComment
|
||||
? tr::lng_gift_upgrade_add_comment(tr::now)
|
||||
: args.canAddSender
|
||||
? tr::lng_gift_upgrade_add_sender(tr::now)
|
||||
: args.canAddMyComment
|
||||
? tr::lng_gift_upgrade_add_my_comment(tr::now)
|
||||
: tr::lng_gift_upgrade_add_my(tr::now)),
|
||||
args.addDetailsDefault),
|
||||
st::defaultCheckbox.margin,
|
||||
style::al_top);
|
||||
checkbox->checkedChanges() | rpl::start_with_next([=](bool checked) {
|
||||
state->preserveDetails = checked;
|
||||
}, checkbox->lifetime());
|
||||
@@ -4936,7 +4938,6 @@ void UpgradeBox(
|
||||
box->setStyle(preview ? st::giftBox : st::upgradeGiftBox);
|
||||
|
||||
const auto cost = args.cost;
|
||||
const auto session = &controller->session();
|
||||
auto buttonText = preview ? tr::lng_box_ok() : rpl::single(QString());
|
||||
const auto button = box->addButton(std::move(buttonText), [=] {
|
||||
if (preview) {
|
||||
@@ -4961,18 +4962,20 @@ void UpgradeBox(
|
||||
UpgradeGift(controller, args.savedId, keepDetails, cost, done);
|
||||
});
|
||||
if (!preview) {
|
||||
auto star = session->data().customEmojiManager().creditsEmoji();
|
||||
auto helper = Ui::Text::CustomEmojiHelper();
|
||||
auto star = helper.paletteDependent(Ui::Earn::IconCreditsEmoji());
|
||||
SetButtonMarkedLabel(
|
||||
button,
|
||||
(cost
|
||||
? tr::lng_gift_upgrade_button(
|
||||
lt_price,
|
||||
rpl::single(star.append(
|
||||
' ' + Lang::FormatCreditsAmountDecimal(
|
||||
CreditsAmount{ cost }))),
|
||||
rpl::single(Ui::Text::IconEmoji(
|
||||
&st::starIconEmoji
|
||||
).append(' ').append(Lang::FormatCreditsAmountDecimal(
|
||||
CreditsAmount{ cost }))),
|
||||
Ui::Text::WithEntities)
|
||||
: tr::lng_gift_upgrade_confirm(Ui::Text::WithEntities)),
|
||||
&controller->session(),
|
||||
helper.context(),
|
||||
st::creditsBoxButtonLabel,
|
||||
&st::giftBox.button.textFg);
|
||||
}
|
||||
|
||||
@@ -641,20 +641,11 @@ void ChangeSetNameBox(
|
||||
const auto it = sets.find(input.id);
|
||||
return (it == sets.end()) ? QString() : it->second->title;
|
||||
}();
|
||||
const auto wrap = box->addRow(object_ptr<Ui::FixedHeightWidget>(
|
||||
const auto field = box->addRow(object_ptr<Ui::InputField>(
|
||||
box,
|
||||
st::editStickerSetNameField.heightMin));
|
||||
auto owned = object_ptr<Ui::InputField>(
|
||||
wrap,
|
||||
st::editStickerSetNameField,
|
||||
tr::lng_stickers_context_edit_name(),
|
||||
wasName);
|
||||
const auto field = owned.data();
|
||||
wrap->widthValue() | rpl::start_with_next([=](int width) {
|
||||
field->move(0, 0);
|
||||
field->resize(width, field->height());
|
||||
wrap->resize(width, field->height());
|
||||
}, wrap->lifetime());
|
||||
wasName));
|
||||
field->selectAll();
|
||||
constexpr auto kMaxSetNameLength = 50;
|
||||
field->setMaxLength(kMaxSetNameLength);
|
||||
|
||||
@@ -248,12 +248,9 @@ void TranslateBox(
|
||||
showText(
|
||||
Ui::Text::Italic(tr::lng_translate_box_error(tr::now)));
|
||||
} else {
|
||||
showText(TextWithEntities{
|
||||
.text = qs(list.front().data().vtext()),
|
||||
.entities = Api::EntitiesFromMTP(
|
||||
&peer->session(),
|
||||
list.front().data().ventities().v),
|
||||
});
|
||||
showText(Api::ParseTextWithEntities(
|
||||
&peer->session(),
|
||||
list.front()));
|
||||
}
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
showText(
|
||||
|
||||
@@ -324,7 +324,7 @@ void UsernamesBox(
|
||||
|
||||
const auto editor = box->addRow(
|
||||
object_ptr<UsernameEditor>(box, peer),
|
||||
{});
|
||||
style::margins());
|
||||
editor->setEnabled(!isBot);
|
||||
box->setFocusCallback([=] { editor->setInnerFocus(); });
|
||||
|
||||
@@ -366,7 +366,7 @@ void UsernamesBox(
|
||||
!isBot
|
||||
? [=] { box->scrollToY(0); editor->setInnerFocus(); }
|
||||
: Fn<void()>(nullptr)),
|
||||
{});
|
||||
style::margins());
|
||||
|
||||
const auto finish = [=] {
|
||||
list->save(
|
||||
|
||||
@@ -825,7 +825,7 @@ void ShowCallsBox(not_null<::Window::SessionController*> window) {
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
box,
|
||||
object_ptr<Ui::VerticalLayout>(box)),
|
||||
{});
|
||||
style::margins());
|
||||
groupCalls->hide(anim::type::instant);
|
||||
groupCalls->toggleOn(state->groupCallsController.shownValue());
|
||||
|
||||
@@ -833,8 +833,7 @@ void ShowCallsBox(not_null<::Window::SessionController*> window) {
|
||||
groupCalls->entity(),
|
||||
tr::lng_call_box_groupcalls_subtitle());
|
||||
state->groupCallsDelegate.setContent(groupCalls->entity()->add(
|
||||
object_ptr<PeerListContent>(box, &state->groupCallsController),
|
||||
{}));
|
||||
object_ptr<PeerListContent>(box, &state->groupCallsController)));
|
||||
state->groupCallsController.setDelegate(&state->groupCallsDelegate);
|
||||
Ui::AddSkip(groupCalls->entity());
|
||||
Ui::AddDivider(groupCalls->entity());
|
||||
@@ -853,7 +852,7 @@ void ShowCallsBox(not_null<::Window::SessionController*> window) {
|
||||
|
||||
const auto content = box->addRow(
|
||||
object_ptr<PeerListContent>(box, &state->callsController),
|
||||
{});
|
||||
style::margins());
|
||||
state->callsDelegate.setContent(content);
|
||||
state->callsController.setDelegate(&state->callsDelegate);
|
||||
|
||||
|
||||
@@ -3982,7 +3982,7 @@ void GroupCall::inviteUsers(
|
||||
}
|
||||
};
|
||||
|
||||
if (const auto call = _conferenceCall.get()) {
|
||||
if (_conferenceCall.get()) {
|
||||
for (const auto &request : requests) {
|
||||
inviteToConference(request, [=] {
|
||||
return &state->result;
|
||||
|
||||
@@ -116,13 +116,12 @@ void ConferenceCallJoinConfirm(
|
||||
st::boxRowPadding + st::confcallLinkHeaderIconPadding);
|
||||
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_confcall_join_title(),
|
||||
st::boxTitle)),
|
||||
st::boxRowPadding + st::confcallLinkTitlePadding);
|
||||
tr::lng_confcall_join_title(),
|
||||
st::boxTitle),
|
||||
st::boxRowPadding + st::confcallLinkTitlePadding,
|
||||
style::al_top);
|
||||
const auto wrapName = [&](not_null<PeerData*> peer) {
|
||||
return rpl::single(Ui::Text::Bold(peer->shortName()));
|
||||
};
|
||||
@@ -136,7 +135,8 @@ void ConferenceCallJoinConfirm(
|
||||
Ui::Text::RichLangValue)
|
||||
: tr::lng_confcall_join_text(Ui::Text::RichLangValue)),
|
||||
st::confcallLinkCenteredText),
|
||||
st::boxRowPadding
|
||||
st::boxRowPadding,
|
||||
style::al_top
|
||||
)->setTryMakeSimilarLines(true);
|
||||
|
||||
const auto &participants = call->participants();
|
||||
@@ -209,7 +209,8 @@ void ConferenceCallJoinConfirm(
|
||||
box,
|
||||
std::move(text),
|
||||
st::confcallLinkCenteredText),
|
||||
st::boxRowPadding
|
||||
st::boxRowPadding,
|
||||
style::al_top
|
||||
)->setTryMakeSimilarLines(true);
|
||||
}
|
||||
const auto joinAndClose = [=] {
|
||||
@@ -321,13 +322,12 @@ void ShowConferenceCallLinkBox(
|
||||
Info::BotStarRef::CreateLinkHeaderIcon(box, &call->session()),
|
||||
st::boxRowPadding + st::confcallLinkHeaderIconPadding);
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_confcall_link_title(),
|
||||
st.box ? st.box->title : st::boxTitle)),
|
||||
st::boxRowPadding + st::confcallLinkTitlePadding);
|
||||
tr::lng_confcall_link_title(),
|
||||
st.box ? st.box->title : st::boxTitle),
|
||||
st::boxRowPadding + st::confcallLinkTitlePadding,
|
||||
style::al_top);
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
@@ -335,7 +335,8 @@ void ShowConferenceCallLinkBox(
|
||||
(st.centerLabel
|
||||
? *st.centerLabel
|
||||
: st::confcallLinkCenteredText)),
|
||||
st::boxRowPadding
|
||||
st::boxRowPadding,
|
||||
style::al_top
|
||||
)->setTryMakeSimilarLines(true);
|
||||
|
||||
Ui::AddSkip(box->verticalLayout(), st::defaultVerticalListSkip * 2);
|
||||
|
||||
@@ -1042,19 +1042,19 @@ not_null<Ui::RpWidget*> CreateReActivateHeader(not_null<QWidget*> parent) {
|
||||
st::boxRowPadding + st::confcallLinkHeaderIconPadding);
|
||||
|
||||
result->add(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
result,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
result,
|
||||
tr::lng_confcall_inactive_title(),
|
||||
st::boxTitle)),
|
||||
st::boxRowPadding + st::confcallLinkTitlePadding);
|
||||
tr::lng_confcall_inactive_title(),
|
||||
st::boxTitle),
|
||||
st::boxRowPadding + st::confcallLinkTitlePadding,
|
||||
style::al_top);
|
||||
result->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
result,
|
||||
tr::lng_confcall_inactive_about(),
|
||||
st::confcallLinkCenteredText),
|
||||
st::boxRowPadding + st::confcallLinkTitlePadding
|
||||
st::boxRowPadding + st::confcallLinkTitlePadding,
|
||||
style::al_top
|
||||
)->setTryMakeSimilarLines(true);
|
||||
Ui::AddDivider(result);
|
||||
|
||||
|
||||
@@ -196,22 +196,13 @@ void RecordingInfo::prepareVideo() {
|
||||
}
|
||||
|
||||
void RecordingInfo::setLabel(const QString &text) {
|
||||
const auto label = _container->add(
|
||||
_container->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
_container,
|
||||
text,
|
||||
st::groupCallRecordingSubLabel),
|
||||
st::groupCallRecordingSubLabelMargins);
|
||||
|
||||
rpl::combine(
|
||||
sizeValue(),
|
||||
label->sizeValue()
|
||||
) | rpl::start_with_next([=](QSize my, QSize labelSize) {
|
||||
label->moveToLeft(
|
||||
(my.width() - labelSize.width()) / 2,
|
||||
label->y(),
|
||||
my.width());
|
||||
}, label->lifetime());
|
||||
st::groupCallRecordingSubLabelMargins,
|
||||
style::al_top);
|
||||
}
|
||||
|
||||
RecordingType RecordingInfo::type() const {
|
||||
|
||||
@@ -126,7 +126,7 @@ int Style::minButtonWidth(HistoryMessageMarkupButton::Type type) const {
|
||||
BotKeyboard::BotKeyboard(
|
||||
not_null<Window::SessionController*> controller,
|
||||
QWidget *parent)
|
||||
: TWidget(parent)
|
||||
: RpWidget(parent)
|
||||
, _controller(controller)
|
||||
, _st(&st::botKbButton) {
|
||||
setGeometry(0, 0, _st->margin, st::botKbScroll.deltat);
|
||||
|
||||
@@ -21,7 +21,7 @@ class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
class BotKeyboard
|
||||
: public TWidget
|
||||
: public Ui::RpWidget
|
||||
, public Ui::AbstractTooltipShower
|
||||
, public ClickHandlerHost {
|
||||
public:
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
void step_selected(crl::time ms, bool timer);
|
||||
void resizeToWidth(int newWidth, int maxOuterHeight) {
|
||||
_maxOuterHeight = maxOuterHeight;
|
||||
return TWidget::resizeToWidth(newWidth);
|
||||
return RpWidget::resizeToWidth(newWidth);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool maximizeSize() const;
|
||||
|
||||
@@ -1295,6 +1295,7 @@ historyRecordLockPosition: point(1px, 22px);
|
||||
historyRecordCancelButtonWidth: 100px;
|
||||
historyRecordCancelButtonFg: lightButtonFg;
|
||||
|
||||
historyRecordTooltipSkip: 8px;
|
||||
historyRecordTooltip: ImportantTooltip(defaultImportantTooltip) {
|
||||
padding: margins(4px, 4px, 4px, 4px);
|
||||
radius: 11px;
|
||||
@@ -1626,4 +1627,17 @@ frozenInfoBox: Box(defaultBox) {
|
||||
shadowIgnoreTopSkip: true;
|
||||
}
|
||||
|
||||
menuTranscribeItemPadding: margins(0px, 10px, 0px, 10px);
|
||||
menuTranscribeDummyButton: IconButton(defaultIconButton) {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
icon: icon {{ "chat/input_attach", historyComposeIconFgOver }};
|
||||
iconOver: icon {{ "chat/input_attach", historyComposeIconFgOver }};
|
||||
|
||||
rippleAreaPosition: point(3px, 3px);
|
||||
rippleAreaSize: 34px;
|
||||
ripple: defaultRippleAnimationBgOver;
|
||||
}
|
||||
|
||||
roundVideoFont: font(14px semibold);
|
||||
|
||||
@@ -704,12 +704,12 @@ void SuggestionsWidget::enterEventHook(QEnterEvent *e) {
|
||||
if (!inner().contains(mapToInner(QCursor::pos()))) {
|
||||
clearMouseSelection();
|
||||
}
|
||||
return TWidget::enterEventHook(e);
|
||||
return RpWidget::enterEventHook(e);
|
||||
}
|
||||
|
||||
void SuggestionsWidget::leaveEventHook(QEvent *e) {
|
||||
clearMouseSelection();
|
||||
return TWidget::leaveEventHook(e);
|
||||
return RpWidget::leaveEventHook(e);
|
||||
}
|
||||
|
||||
SuggestionsController::SuggestionsController(
|
||||
|
||||
@@ -1488,7 +1488,8 @@ void FrozenInfoBox(
|
||||
content,
|
||||
tr::lng_frozen_title(),
|
||||
st.title ? *st.title : st::uniqueGiftTitle),
|
||||
st::settingsPremiumRowTitlePadding);
|
||||
st::settingsPremiumRowTitlePadding,
|
||||
style::al_top);
|
||||
|
||||
Ui::AddSkip(content, st::defaultVerticalListSkip * 3);
|
||||
|
||||
|
||||
@@ -277,7 +277,7 @@ void TabbedPanel::leaveEventHook(QEvent *e) {
|
||||
} else {
|
||||
_hideTimer.callOnce(kHideTimeoutMs);
|
||||
}
|
||||
return TWidget::leaveEventHook(e);
|
||||
return RpWidget::leaveEventHook(e);
|
||||
}
|
||||
|
||||
void TabbedPanel::otherEnter() {
|
||||
|
||||
@@ -658,8 +658,20 @@ bool Application::eventFilter(QObject *object, QEvent *e) {
|
||||
updateNonIdle();
|
||||
} break;
|
||||
|
||||
case QEvent::KeyRelease: {
|
||||
const auto event = static_cast<QKeyEvent*>(e);
|
||||
if (Shortcuts::HandlePossibleChatSwitch(event)) {
|
||||
return true;
|
||||
}
|
||||
} break;
|
||||
|
||||
case QEvent::ShortcutOverride: {
|
||||
// handle shortcuts ourselves
|
||||
// Ctrl+Tab/Ctrl+Shift+Tab chat switch is a special shortcut case,
|
||||
// because it not only does an action on the shortcut activation,
|
||||
// but also keeps the UI visible until you release the Ctrl key.
|
||||
Shortcuts::HandlePossibleChatSwitch(static_cast<QKeyEvent*>(e));
|
||||
|
||||
// Handle all the shortcut management manually.
|
||||
return true;
|
||||
} break;
|
||||
|
||||
|
||||
@@ -240,7 +240,8 @@ QByteArray Settings::serialize() const {
|
||||
+ Serialize::stringSize(_customFontFamily)
|
||||
+ sizeof(qint32) * 3
|
||||
+ Serialize::bytearraySize(_tonsiteStorageToken)
|
||||
+ sizeof(qint32) * 8;
|
||||
+ sizeof(qint32) * 8
|
||||
+ sizeof(ushort);
|
||||
|
||||
// Fork Settings.
|
||||
size += sizeof(qint32);
|
||||
@@ -429,7 +430,8 @@ QByteArray Settings::serialize() const {
|
||||
<< SerializeVideoQuality(_videoQuality)
|
||||
<< qint32(_ivZoom.current())
|
||||
<< qint32(_systemDarkModeEnabled.current() ? 1 : 0)
|
||||
<< qint32(_quickDialogAction);
|
||||
<< qint32(_quickDialogAction)
|
||||
<< _notificationsVolume;
|
||||
}
|
||||
|
||||
Ensures(result.size() == size);
|
||||
@@ -572,6 +574,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||
quint32 videoQuality = SerializeVideoQuality(_videoQuality);
|
||||
quint32 chatFiltersHorizontal = _chatFiltersHorizontal.current() ? 1 : 0;
|
||||
quint32 quickDialogAction = quint32(_quickDialogAction);
|
||||
ushort notificationsVolume = _notificationsVolume;
|
||||
|
||||
stream >> themesAccentColors;
|
||||
if (!stream.atEnd()) {
|
||||
@@ -916,6 +919,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||
if (!stream.atEnd()) {
|
||||
stream >> quickDialogAction;
|
||||
}
|
||||
if (!stream.atEnd()) {
|
||||
stream >> notificationsVolume;
|
||||
}
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
LOG(("App Error: "
|
||||
"Bad data for Core::Settings::constructFromSerialized()"));
|
||||
@@ -1152,6 +1158,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
|
||||
_videoQuality = DeserializeVideoQuality(videoQuality);
|
||||
_chatFiltersHorizontal = (chatFiltersHorizontal == 1);
|
||||
_quickDialogAction = Dialogs::Ui::QuickDialogAction(quickDialogAction);
|
||||
_notificationsVolume = notificationsVolume;
|
||||
}
|
||||
|
||||
QString Settings::getSoundPath(const QString &key) const {
|
||||
@@ -1546,6 +1553,7 @@ void Settings::resetOnLastLogout() {
|
||||
_videoQuality = {};
|
||||
_chatFiltersHorizontal = false;
|
||||
_quickDialogAction = Dialogs::Ui::QuickDialogAction::Disabled;
|
||||
_notificationsVolume = 100;
|
||||
|
||||
_recentEmojiPreload.clear();
|
||||
_recentEmoji.clear();
|
||||
|
||||
@@ -956,6 +956,13 @@ public:
|
||||
[[nodiscard]] Dialogs::Ui::QuickDialogAction quickDialogAction() const;
|
||||
void setQuickDialogAction(Dialogs::Ui::QuickDialogAction);
|
||||
|
||||
[[nodiscard]] ushort notificationsVolume() const {
|
||||
return _notificationsVolume;
|
||||
}
|
||||
void setNotificationsVolume(ushort value) {
|
||||
_notificationsVolume = value;
|
||||
}
|
||||
|
||||
void resetOnLastLogout();
|
||||
|
||||
private:
|
||||
@@ -1101,6 +1108,8 @@ private:
|
||||
Dialogs::Ui::QuickDialogAction _quickDialogAction
|
||||
= Dialogs::Ui::QuickDialogAction::Disabled;
|
||||
|
||||
ushort _notificationsVolume = 100;
|
||||
|
||||
QByteArray _photoEditorBrush;
|
||||
|
||||
};
|
||||
|
||||
@@ -30,6 +30,10 @@ constexpr auto kCountLimit = 256; // How many shortcuts can be in json file.
|
||||
rpl::event_stream<not_null<Request*>> RequestsStream;
|
||||
bool Paused/* = false*/;
|
||||
|
||||
Qt::Key ChatSwitchModifier/* = Qt::Key()*/;
|
||||
bool ChatSwitchStarted/* = false*/;
|
||||
rpl::event_stream<ChatSwitchRequest> ChatSwitchStream;
|
||||
|
||||
const auto AutoRepeatCommands = base::flat_set<Command>{
|
||||
Command::MediaPrevious,
|
||||
Command::MediaNext,
|
||||
@@ -112,6 +116,8 @@ const auto CommandByName = base::flat_map<QString, Command>{
|
||||
{ u"show_chat_menu"_q , Command::ShowChatMenu },
|
||||
{ u"show_chat_preview"_q , Command::ShowChatPreview },
|
||||
|
||||
{ u"record_voice"_q , Command::RecordVoice },
|
||||
|
||||
// Shortcuts that have no default values.
|
||||
{ u"message"_q , Command::JustSendMessage },
|
||||
{ u"message_silently"_q , Command::SendSilentMessage },
|
||||
@@ -119,6 +125,7 @@ const auto CommandByName = base::flat_map<QString, Command>{
|
||||
{ u"media_viewer_video_fullscreen"_q , Command::MediaViewerFullscreen },
|
||||
{ u"show_scheduled"_q , Command::ShowScheduled },
|
||||
{ u"archive_chat"_q , Command::ArchiveChat },
|
||||
{ u"record_round"_q , Command::RecordRound },
|
||||
//
|
||||
};
|
||||
|
||||
@@ -140,6 +147,7 @@ const base::flat_map<Command, QString> &CommandNames() {
|
||||
Command::MediaViewerFullscreen,
|
||||
Command::ShowScheduled,
|
||||
Command::ArchiveChat,
|
||||
Command::RecordRound,
|
||||
};
|
||||
|
||||
class Manager {
|
||||
@@ -152,6 +160,7 @@ public:
|
||||
void toggleMedia(bool toggled);
|
||||
void toggleSupport(bool toggled);
|
||||
void listen(not_null<QWidget*> widget);
|
||||
[[nodiscard]] bool handles(const QKeySequence &sequence) const;
|
||||
|
||||
[[nodiscard]] const QStringList &errors() const;
|
||||
|
||||
@@ -357,6 +366,10 @@ void Manager::listen(not_null<QWidget*> widget) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Manager::handles(const QKeySequence &sequence) const {
|
||||
return _shortcuts.contains(sequence);
|
||||
}
|
||||
|
||||
void Manager::pruneListened() {
|
||||
for (auto i = begin(_listened); i != end(_listened);) {
|
||||
if (i->data()) {
|
||||
@@ -466,10 +479,6 @@ void Manager::fillDefaults() {
|
||||
set(u"ctrl+pgup"_q, Command::ChatPrevious);
|
||||
set(u"alt+up"_q, Command::ChatPrevious);
|
||||
|
||||
set(u"%1+tab"_q.arg(ctrl), Command::ChatNext);
|
||||
set(u"%1+shift+tab"_q.arg(ctrl), Command::ChatPrevious);
|
||||
set(u"%1+backtab"_q.arg(ctrl), Command::ChatPrevious);
|
||||
|
||||
set(u"ctrl+alt+home"_q, Command::ChatFirst);
|
||||
set(u"ctrl+alt+end"_q, Command::ChatLast);
|
||||
|
||||
@@ -509,6 +518,8 @@ void Manager::fillDefaults() {
|
||||
set(u"ctrl+\\"_q, Command::ShowChatMenu);
|
||||
set(u"ctrl+]"_q, Command::ShowChatPreview);
|
||||
|
||||
set(u"ctrl+r"_q, Command::RecordVoice);
|
||||
|
||||
_defaults = keysCurrents();
|
||||
}
|
||||
|
||||
@@ -794,6 +805,72 @@ bool HandleEvent(
|
||||
return Launch(Data.lookup(object));
|
||||
}
|
||||
|
||||
void CancelChatSwitch(Qt::Key result) {
|
||||
ChatSwitchModifier = Qt::Key();
|
||||
if (ChatSwitchStarted) {
|
||||
ChatSwitchStarted = false;
|
||||
ChatSwitchStream.fire({ .action = result });
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<ChatSwitchRequest> ChatSwitchRequests() {
|
||||
return ChatSwitchStream.events();
|
||||
}
|
||||
|
||||
bool HandlePossibleChatSwitch(not_null<QKeyEvent*> event) {
|
||||
const auto type = event->type();
|
||||
if (Paused) {
|
||||
return false;
|
||||
} else if (type == QEvent::ShortcutOverride) {
|
||||
const auto key = Qt::Key(event->key());
|
||||
if (key == Qt::Key_Escape) {
|
||||
CancelChatSwitch(Qt::Key_Escape);
|
||||
return false;
|
||||
} else if (key == Qt::Key_Return || key == Qt::Key_Enter) {
|
||||
CancelChatSwitch(Qt::Key_Enter);
|
||||
return false;
|
||||
}
|
||||
const auto ctrl = Platform::IsMac()
|
||||
? Qt::MetaModifier
|
||||
: Qt::ControlModifier;
|
||||
|
||||
if (Data.handles(ctrl | Qt::ShiftModifier | Qt::Key_Tab)
|
||||
&& Data.handles(QKeySequence(ctrl | Qt::Key_Tab))
|
||||
&& Data.handles(QKeySequence(ctrl | Qt::Key_Backtab))) {
|
||||
return false;
|
||||
} else if (key == Qt::Key_Control || key == Qt::Key_Meta) {
|
||||
ChatSwitchModifier = key;
|
||||
} else if (key == Qt::Key_Tab || key == Qt::Key_Backtab) {
|
||||
const auto modifiers = event->modifiers();
|
||||
if (modifiers & ctrl) {
|
||||
if (Data.handles(modifiers | key)) {
|
||||
return false;
|
||||
}
|
||||
if (ChatSwitchModifier == Qt::Key()) {
|
||||
ChatSwitchModifier = Platform::IsMac()
|
||||
? Qt::Key_Meta
|
||||
: Qt::Key_Control;
|
||||
}
|
||||
const auto action = (modifiers & Qt::ShiftModifier)
|
||||
? Qt::Key_Backtab
|
||||
: key;
|
||||
const auto started = !std::exchange(ChatSwitchStarted, true);
|
||||
ChatSwitchStream.fire({
|
||||
.action = action,
|
||||
.started = started,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (type == QEvent::KeyRelease) {
|
||||
const auto key = Qt::Key(event->key());
|
||||
if (key == ChatSwitchModifier) {
|
||||
CancelChatSwitch(Qt::Key_Enter);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ToggleMediaShortcuts(bool toggled) {
|
||||
Data.toggleMedia(toggled);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
class QKeyEvent;
|
||||
class QShortcutEvent;
|
||||
|
||||
namespace Shortcuts {
|
||||
|
||||
enum class Command {
|
||||
@@ -66,6 +69,9 @@ enum class Command {
|
||||
SendSilentMessage,
|
||||
ScheduleMessage,
|
||||
|
||||
RecordVoice,
|
||||
RecordRound,
|
||||
|
||||
ReadChat,
|
||||
ArchiveChat,
|
||||
|
||||
@@ -119,7 +125,7 @@ private:
|
||||
|
||||
};
|
||||
|
||||
rpl::producer<not_null<Request*>> Requests();
|
||||
[[nodiscard]] rpl::producer<not_null<Request*>> Requests();
|
||||
|
||||
void Start();
|
||||
void Finish();
|
||||
@@ -129,7 +135,15 @@ void Listen(not_null<QWidget*> widget);
|
||||
bool Launch(Command command);
|
||||
bool HandleEvent(not_null<QObject*> object, not_null<QShortcutEvent*> event);
|
||||
|
||||
const QStringList &Errors();
|
||||
bool HandlePossibleChatSwitch(not_null<QKeyEvent*> event);
|
||||
|
||||
struct ChatSwitchRequest {
|
||||
Qt::Key action = Qt::Key_Tab; // Key_Tab, Key_Backtab or Key_Escape.
|
||||
bool started = false;
|
||||
};
|
||||
[[nodiscard]] rpl::producer<ChatSwitchRequest> ChatSwitchRequests();
|
||||
|
||||
[[nodiscard]] const QStringList &Errors();
|
||||
|
||||
// Media shortcuts are not enabled by default, because other
|
||||
// applications also use them. They are enabled only when
|
||||
|
||||
@@ -24,7 +24,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
|
||||
constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
|
||||
constexpr auto AppName = "Telegram Desktop"_cs;
|
||||
constexpr auto AppFile = "Telegram"_cs;
|
||||
constexpr auto AppVersion = 6000002;
|
||||
constexpr auto AppVersionStr = "6.0.2";
|
||||
constexpr auto AppBetaVersion = false;
|
||||
constexpr auto AppVersion = 6000003;
|
||||
constexpr auto AppVersionStr = "6.0.3";
|
||||
constexpr auto AppBetaVersion = true;
|
||||
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
||||
|
||||
@@ -7,6 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "data/components/recent_peers.h"
|
||||
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_session.h"
|
||||
#include "history/history.h"
|
||||
#include "main/main_session.h"
|
||||
#include "storage/serialize_common.h"
|
||||
#include "storage/serialize_peer.h"
|
||||
@@ -16,6 +19,7 @@ namespace Data {
|
||||
namespace {
|
||||
|
||||
constexpr auto kLimit = 48;
|
||||
constexpr auto kMaxRememberedOpenChats = 32;
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -133,4 +137,40 @@ void RecentPeers::applyLocal(QByteArray serialized) {
|
||||
("Suggestions: RecentPeers read OK, count: %1").arg(_list.size()));
|
||||
}
|
||||
|
||||
std::vector<not_null<Thread*>> RecentPeers::collectChatOpenHistory() const {
|
||||
_session->local().readSearchSuggestions();
|
||||
|
||||
auto result = _opens;
|
||||
result.reserve(result.size() + _list.size());
|
||||
for (const auto &peer : _list) {
|
||||
const auto history = peer->owner().history(peer);
|
||||
const auto thread = not_null<Data::Thread*>(history);
|
||||
if (!ranges::contains(result, thread)) {
|
||||
result.push_back(thread);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void RecentPeers::chatOpenPush(not_null<Thread*> thread) {
|
||||
const auto i = ranges::find(_opens, thread);
|
||||
if (i == end(_opens)) {
|
||||
while (_opens.size() >= kMaxRememberedOpenChats) {
|
||||
_opens.pop_back();
|
||||
}
|
||||
_opens.insert(begin(_opens), thread);
|
||||
} else if (i != begin(_opens)) {
|
||||
ranges::rotate(begin(_opens), i, i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void RecentPeers::chatOpenDestroyed(not_null<Thread*> thread) {
|
||||
_opens.erase(ranges::remove(_opens, thread), end(_opens));
|
||||
}
|
||||
|
||||
void RecentPeers::chatOpenKeepUserpics(
|
||||
base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> userpics) {
|
||||
_chatOpenUserpicsCache = std::move(userpics);
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
||||
@@ -7,12 +7,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/userpic_view.h"
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Data {
|
||||
|
||||
class Thread;
|
||||
|
||||
class RecentPeers final {
|
||||
public:
|
||||
explicit RecentPeers(not_null<Main::Session*> session);
|
||||
@@ -28,10 +32,22 @@ public:
|
||||
[[nodiscard]] QByteArray serialize() const;
|
||||
void applyLocal(QByteArray serialized);
|
||||
|
||||
[[nodiscard]] auto collectChatOpenHistory() const
|
||||
-> std::vector<not_null<Thread*>>;
|
||||
void chatOpenPush(not_null<Thread*> thread);
|
||||
void chatOpenDestroyed(not_null<Thread*> thread);
|
||||
void chatOpenKeepUserpics(
|
||||
base::flat_map<not_null<PeerData*>, Ui::PeerUserpicView> userpics);
|
||||
|
||||
private:
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
std::vector<not_null<PeerData*>> _list;
|
||||
std::vector<not_null<Thread*>> _opens;
|
||||
base::flat_map<
|
||||
not_null<PeerData*>,
|
||||
Ui::PeerUserpicView> _chatOpenUserpicsCache;
|
||||
|
||||
rpl::event_stream<> _updates;
|
||||
|
||||
};
|
||||
|
||||
@@ -235,7 +235,8 @@ void ChannelData::setFlags(ChannelDataFlags which) {
|
||||
| Flag::CallNotEmpty
|
||||
| Flag::SimilarExpanded
|
||||
| Flag::Signatures
|
||||
| Flag::SignatureProfiles)) {
|
||||
| Flag::SignatureProfiles
|
||||
| Flag::ForumTabs)) {
|
||||
if (const auto history = this->owner().historyLoaded(this)) {
|
||||
if (diff & Flag::CallNotEmpty) {
|
||||
history->updateChatListEntry();
|
||||
@@ -262,6 +263,9 @@ void ChannelData::setFlags(ChannelDataFlags which) {
|
||||
if (diff & (Flag::Signatures | Flag::SignatureProfiles)) {
|
||||
session().changes().peerUpdated(this, UpdateFlag::Rights);
|
||||
}
|
||||
if (diff & Flag::ForumTabs) {
|
||||
history->forumTabsChanged(which & Flag::ForumTabs);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (const auto raw = takenForum.get()) {
|
||||
|
||||
@@ -98,6 +98,7 @@ struct CreditsHistoryEntry final {
|
||||
bool converted : 1 = false;
|
||||
bool anonymous : 1 = false;
|
||||
bool stargift : 1 = false;
|
||||
bool postsSearch : 1 = false;
|
||||
bool giftTransferred : 1 = false;
|
||||
bool giftRefunded : 1 = false;
|
||||
bool giftUpgraded : 1 = false;
|
||||
|
||||
@@ -7,12 +7,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "data/data_forum.h"
|
||||
|
||||
#include "data/components/recent_peers.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_histories.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_forum_icons.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_replies_list.h"
|
||||
#include "data/notify/data_notify_settings.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
@@ -193,6 +195,7 @@ void Forum::applyTopicDeleted(MsgId rootId) {
|
||||
_activeSubsectionTopic = nullptr;
|
||||
}
|
||||
_topicDestroyed.fire(raw);
|
||||
_history->session().recentPeers().chatOpenDestroyed(raw);
|
||||
session().changes().topicUpdated(
|
||||
raw,
|
||||
Data::TopicUpdate::Flag::Destroyed);
|
||||
@@ -276,6 +279,33 @@ Thread *Forum::activeSubsectionThread() const {
|
||||
return _activeSubsectionTopic;
|
||||
}
|
||||
|
||||
void Forum::markUnreadCountsUnknown(MsgId readTillId) {
|
||||
if (!channel()->useSubsectionTabs()) {
|
||||
return;
|
||||
}
|
||||
for (const auto &[rootId, topic] : _topics) {
|
||||
const auto replies = topic->replies();
|
||||
if (replies->unreadCountCurrent() > 0) {
|
||||
replies->setInboxReadTill(readTillId, std::nullopt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Forum::updateUnreadCounts(
|
||||
MsgId readTillId,
|
||||
const base::flat_map<not_null<ForumTopic*>, int> &counts) {
|
||||
if (!channel()->useSubsectionTabs()) {
|
||||
return;
|
||||
}
|
||||
for (const auto &[rootId, topic] : _topics) {
|
||||
const auto raw = topic.get();
|
||||
const auto replies = raw->replies();
|
||||
const auto i = counts.find(raw);
|
||||
const auto count = (i != end(counts)) ? i->second : 0;
|
||||
replies->setInboxReadTill(readTillId, count);
|
||||
}
|
||||
}
|
||||
|
||||
void Forum::listMessageChanged(HistoryItem *from, HistoryItem *to) {
|
||||
if (from || to) {
|
||||
reorderLastTopics();
|
||||
|
||||
@@ -99,6 +99,11 @@ public:
|
||||
void saveActiveSubsectionThread(not_null<Thread*> thread);
|
||||
[[nodiscard]] Thread *activeSubsectionThread() const;
|
||||
|
||||
void markUnreadCountsUnknown(MsgId readTillId);
|
||||
void updateUnreadCounts(
|
||||
MsgId readTillId,
|
||||
const base::flat_map<not_null<ForumTopic*>, int> &counts);
|
||||
|
||||
[[nodiscard]] rpl::lifetime &lifetime() {
|
||||
return _lifetime;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/painter.h"
|
||||
#include "ui/color_int_conversion.h"
|
||||
#include "ui/text/text_custom_emoji.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
|
||||
@@ -756,6 +757,16 @@ TextWithEntities ForumTopic::titleWithIcon() const {
|
||||
return ForumTopicIconWithTitle(_rootId, _iconId, _title);
|
||||
}
|
||||
|
||||
TextWithEntities ForumTopic::titleWithIconOrLogo() const {
|
||||
if (_iconId || isGeneral()) {
|
||||
return titleWithIcon();
|
||||
}
|
||||
return Ui::Text::SingleCustomEmoji(Data::TopicIconEmojiEntity({
|
||||
.title = _title,
|
||||
.colorId = _colorId,
|
||||
})).append(' ').append(_title);
|
||||
}
|
||||
|
||||
int ForumTopic::titleVersion() const {
|
||||
return _titleVersion;
|
||||
}
|
||||
|
||||
@@ -148,6 +148,7 @@ public:
|
||||
|
||||
[[nodiscard]] QString title() const;
|
||||
[[nodiscard]] TextWithEntities titleWithIcon() const;
|
||||
[[nodiscard]] TextWithEntities titleWithIconOrLogo() const;
|
||||
[[nodiscard]] int titleVersion() const;
|
||||
void applyTitle(const QString &title);
|
||||
[[nodiscard]] DocumentId iconId() const;
|
||||
|
||||
@@ -713,7 +713,7 @@ void Histories::sendReadRequest(not_null<History*> history, State &state) {
|
||||
} else {
|
||||
Assert(!state->sentReadTill || state->sentReadTill > tillId);
|
||||
}
|
||||
history->validateMonoforumUnread(tillId);
|
||||
history->validateMonoAndForumUnread(tillId);
|
||||
sendReadRequests();
|
||||
finish();
|
||||
};
|
||||
|
||||
@@ -2232,8 +2232,7 @@ ItemPreview MediaInvoice::toPreview(ToPreviewOptions options) const {
|
||||
? parent()->translatedText()
|
||||
: parent()->originalText());
|
||||
const auto hasMiniImages = !images.empty();
|
||||
auto nice = Ui::Text::Colorized(
|
||||
Ui::CreditsEmojiSmall(&parent()->history()->session()));
|
||||
auto nice = Ui::Text::Colorized(Ui::CreditsEmojiSmall());
|
||||
nice.append(WithCaptionNotificationText(type, caption, hasMiniImages));
|
||||
return {
|
||||
.text = std::move(nice),
|
||||
|
||||
@@ -70,12 +70,9 @@ bool PollData::closeByTimer() {
|
||||
bool PollData::applyChanges(const MTPDpoll &poll) {
|
||||
Expects(poll.vid().v == id);
|
||||
|
||||
const auto newQuestion = TextWithEntities{
|
||||
.text = qs(poll.vquestion().data().vtext()),
|
||||
.entities = Api::EntitiesFromMTP(
|
||||
&session(),
|
||||
poll.vquestion().data().ventities().v),
|
||||
};
|
||||
const auto newQuestion = Api::ParseTextWithEntities(
|
||||
&session(),
|
||||
poll.vquestion());
|
||||
const auto newFlags = (poll.is_closed() ? Flag::Closed : Flag(0))
|
||||
| (poll.is_public_voters() ? Flag::PublicVotes : Flag(0))
|
||||
| (poll.is_multiple_choice() ? Flag::MultiChoice : Flag(0))
|
||||
@@ -88,12 +85,9 @@ bool PollData::applyChanges(const MTPDpoll &poll) {
|
||||
return data.match([&](const MTPDpollAnswer &answer) {
|
||||
auto result = PollAnswer();
|
||||
result.option = answer.voption().v;
|
||||
result.text = TextWithEntities{
|
||||
.text = qs(answer.vtext().data().vtext()),
|
||||
.entities = Api::EntitiesFromMTP(
|
||||
&session(),
|
||||
answer.vtext().data().ventities().v),
|
||||
};
|
||||
result.text = Api::ParseTextWithEntities(
|
||||
&session(),
|
||||
answer.vtext());
|
||||
return result;
|
||||
});
|
||||
}) | ranges::views::take(
|
||||
|
||||
@@ -16,6 +16,7 @@ struct PremiumSubscriptionOption {
|
||||
QString costPerMonth;
|
||||
QString costNoDiscount;
|
||||
QString costTotal;
|
||||
QString currency;
|
||||
QString total;
|
||||
QString botUrl;
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "core/application.h"
|
||||
#include "data/components/recent_peers.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_histories.h"
|
||||
@@ -452,6 +453,7 @@ void SavedMessages::applySublistDeleted(not_null<PeerData*> sublistPeer) {
|
||||
}
|
||||
|
||||
_sublistDestroyed.fire(raw);
|
||||
_owner->session().recentPeers().chatOpenDestroyed(raw);
|
||||
session().changes().sublistUpdated(
|
||||
raw,
|
||||
Data::SublistUpdate::Flag::Destroyed);
|
||||
|
||||
@@ -45,12 +45,9 @@ Main::Session &TodoListData::session() const {
|
||||
}
|
||||
|
||||
bool TodoListData::applyChanges(const MTPDtodoList &todolist) {
|
||||
const auto newTitle = TextWithEntities{
|
||||
.text = qs(todolist.vtitle().data().vtext()),
|
||||
.entities = Api::EntitiesFromMTP(
|
||||
&session(),
|
||||
todolist.vtitle().data().ventities().v),
|
||||
};
|
||||
const auto newTitle = Api::ParseTextWithEntities(
|
||||
&session(),
|
||||
todolist.vtitle());
|
||||
const auto newFlags = (todolist.is_others_can_append()
|
||||
? Flag::OthersCanAppend
|
||||
: Flag())
|
||||
@@ -222,12 +219,7 @@ TodoListItem TodoListItemFromMTP(
|
||||
const MTPTodoItem &item) {
|
||||
const auto &data = item.data();
|
||||
return {
|
||||
.text = TextWithEntities{
|
||||
.text = qs(data.vtitle().data().vtext()),
|
||||
.entities = Api::EntitiesFromMTP(
|
||||
session,
|
||||
data.vtitle().data().ventities().v),
|
||||
},
|
||||
.text = Api::ParseTextWithEntities(session, data.vtitle()),
|
||||
.id = data.vid().v,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -21,11 +21,6 @@ class Thread;
|
||||
class Forum;
|
||||
class ForumTopic;
|
||||
|
||||
enum class DefaultNotify {
|
||||
User,
|
||||
Group,
|
||||
Broadcast,
|
||||
};
|
||||
[[nodiscard]] DefaultNotify DefaultNotifyType(
|
||||
not_null<const PeerData*> peer);
|
||||
|
||||
|
||||
@@ -11,6 +11,12 @@ namespace Data {
|
||||
|
||||
class NotifyPeerSettingsValue;
|
||||
|
||||
enum class DefaultNotify : uint8_t {
|
||||
User,
|
||||
Group,
|
||||
Broadcast,
|
||||
};
|
||||
|
||||
struct NotifySound {
|
||||
QString title;
|
||||
QString data;
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
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 "data/notify/data_peer_notify_volume.h"
|
||||
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_thread.h"
|
||||
#include "data/notify/data_peer_notify_settings.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_session_settings.h"
|
||||
#include "core/application.h"
|
||||
#include "window/notifications_manager.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "styles/style_settings.h"
|
||||
|
||||
namespace Data {
|
||||
|
||||
Data::VolumeController DefaultRingtonesVolumeController(
|
||||
not_null<Main::Session*> session,
|
||||
Data::DefaultNotify defaultNotify) {
|
||||
return Data::VolumeController{
|
||||
.volume = [=]() -> ushort {
|
||||
const auto volume = session->settings().ringtoneVolume(
|
||||
defaultNotify);
|
||||
return volume ? volume : 100;
|
||||
},
|
||||
.saveVolume = [=](ushort volume) {
|
||||
session->settings().setRingtoneVolume(defaultNotify, volume);
|
||||
session->saveSettingsDelayed();
|
||||
}};
|
||||
}
|
||||
|
||||
Data::VolumeController ThreadRingtonesVolumeController(
|
||||
not_null<Data::Thread*> thread) {
|
||||
return Data::VolumeController{
|
||||
.volume = [=]() -> ushort {
|
||||
const auto volume = thread->session().settings().ringtoneVolume(
|
||||
thread->peer()->id,
|
||||
thread->topicRootId(),
|
||||
thread->monoforumPeerId());
|
||||
return volume ? volume : 100;
|
||||
},
|
||||
.saveVolume = [=](ushort volume) {
|
||||
thread->session().settings().setRingtoneVolume(
|
||||
thread->peer()->id,
|
||||
thread->topicRootId(),
|
||||
thread->monoforumPeerId(),
|
||||
volume);
|
||||
thread->session().saveSettingsDelayed();
|
||||
}};
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
|
||||
void AddRingtonesVolumeSlider(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
rpl::producer<bool> toggleOn,
|
||||
rpl::producer<QString> subtitle,
|
||||
Data::VolumeController volumeController) {
|
||||
Expects(volumeController.volume && volumeController.saveVolume);
|
||||
|
||||
const auto volumeWrap = container->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
container,
|
||||
object_ptr<Ui::VerticalLayout>(container)));
|
||||
volumeWrap->toggleOn(
|
||||
rpl::combine(
|
||||
Core::App().notifications().volumeSupportedValue(),
|
||||
std::move(toggleOn)
|
||||
) | rpl::map(
|
||||
rpl::mappers::_1 && rpl::mappers::_2
|
||||
) | rpl::distinct_until_changed(),
|
||||
anim::type::normal);
|
||||
volumeWrap->finishAnimating();
|
||||
|
||||
Ui::AddSubsectionTitle(volumeWrap->entity(), std::move(subtitle));
|
||||
auto sliderWithLabel = Settings::MakeSliderWithLabel(
|
||||
volumeWrap->entity(),
|
||||
st::settingsScale,
|
||||
st::settingsScaleLabel,
|
||||
st::normalFont->spacew * 2,
|
||||
st::settingsScaleLabel.style.font->width("100%"),
|
||||
true);
|
||||
const auto slider = sliderWithLabel.slider;
|
||||
const auto label = sliderWithLabel.label;
|
||||
|
||||
volumeWrap->entity()->add(
|
||||
std::move(sliderWithLabel.widget),
|
||||
st::settingsBigScalePadding);
|
||||
|
||||
const auto updateLabel = [=](int volume) {
|
||||
label->setText(QString::number(volume) + '%');
|
||||
};
|
||||
|
||||
slider->setPseudoDiscrete(
|
||||
100,
|
||||
[=](int index) { return index + 1; },
|
||||
int(volumeController.volume()),
|
||||
updateLabel,
|
||||
[saveVolume = volumeController.saveVolume](int volume) {
|
||||
saveVolume(volume);
|
||||
});
|
||||
updateLabel(volumeController.volume());
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
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
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Ui {
|
||||
class VerticalLayout;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Data {
|
||||
|
||||
enum class DefaultNotify : uint8_t;
|
||||
class Thread;
|
||||
|
||||
struct VolumeController {
|
||||
Fn<ushort()> volume = nullptr;
|
||||
Fn<void(ushort)> saveVolume = nullptr;
|
||||
};
|
||||
|
||||
[[nodiscard]] VolumeController DefaultRingtonesVolumeController(
|
||||
not_null<Main::Session*> session,
|
||||
Data::DefaultNotify defaultNotify);
|
||||
|
||||
[[nodiscard]] VolumeController ThreadRingtonesVolumeController(
|
||||
not_null<Data::Thread*> thread);
|
||||
|
||||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
|
||||
void AddRingtonesVolumeSlider(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
rpl::producer<bool> toggleOn,
|
||||
rpl::producer<QString> subtitle,
|
||||
Data::VolumeController volumeController);
|
||||
|
||||
} // namespace Ui
|
||||
@@ -41,7 +41,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "apiwrap.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_credits.h" // giftBoxByStarsStyle
|
||||
|
||||
namespace Data {
|
||||
namespace {
|
||||
@@ -101,10 +100,6 @@ private:
|
||||
: FrameSizeFromTag(tag);
|
||||
}
|
||||
|
||||
[[nodiscard]] QString InternalPrefix() {
|
||||
return u"internal:"_q;
|
||||
}
|
||||
|
||||
[[nodiscard]] QString UserpicEmojiPrefix() {
|
||||
return u"userpic:"_q;
|
||||
}
|
||||
@@ -570,8 +565,6 @@ std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create(
|
||||
const auto original = data.mid(ForceStaticPrefix().size());
|
||||
return std::make_unique<Ui::Text::FirstFrameEmoji>(
|
||||
create(original, std::move(update), tag, sizeOverride));
|
||||
} else if (data.startsWith(InternalPrefix())) {
|
||||
return internal(data);
|
||||
} else if (data.startsWith(UserpicEmojiPrefix())) {
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
const auto size = EmojiSizeFromTag(tag) / ratio;
|
||||
@@ -619,26 +612,6 @@ std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create(
|
||||
});
|
||||
}
|
||||
|
||||
std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::internal(
|
||||
QStringView data) {
|
||||
const auto v = data.mid(InternalPrefix().size()).split(',');
|
||||
if (v.size() != 5 && v.size() != 1) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto index = v[0].toInt();
|
||||
Assert(index >= 0 && index < _internalEmoji.size());
|
||||
|
||||
auto &info = _internalEmoji[index];
|
||||
const auto padding = (v.size() == 5)
|
||||
? QMargins(v[1].toInt(), v[2].toInt(), v[3].toInt(), v[4].toInt())
|
||||
: QMargins();
|
||||
return std::make_unique<Ui::CustomEmoji::Internal>(
|
||||
data.toString(),
|
||||
info.image,
|
||||
padding,
|
||||
info.textColor);
|
||||
}
|
||||
|
||||
std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::userpic(
|
||||
QStringView data,
|
||||
Fn<void()> update,
|
||||
@@ -1021,65 +994,6 @@ uint64 CustomEmojiManager::coloredSetId() const {
|
||||
return _coloredSetId;
|
||||
}
|
||||
|
||||
TextWithEntities CustomEmojiManager::creditsEmoji(QMargins padding) {
|
||||
return Ui::Text::SingleCustomEmoji(registerInternalEmoji(
|
||||
u"builtin:credits_emoji"_q,
|
||||
Ui::GenerateStars(st::normalFont->height, 1),
|
||||
padding,
|
||||
false));
|
||||
}
|
||||
|
||||
TextWithEntities CustomEmojiManager::ministarEmoji(QMargins padding) {
|
||||
return Ui::Text::SingleCustomEmoji(registerInternalEmoji(
|
||||
u"builtin:ministar_emoji"_q,
|
||||
Ui::GenerateStars(st::giftBoxByStarsStyle.font->height, 1),
|
||||
padding,
|
||||
false));
|
||||
}
|
||||
|
||||
QString CustomEmojiManager::registerInternalEmoji(
|
||||
const QString &key,
|
||||
QImage emoji,
|
||||
QMargins padding,
|
||||
bool textColor) {
|
||||
auto i = _imageEmoji.find(key);
|
||||
if (i == end(_imageEmoji)) {
|
||||
i = _imageEmoji.emplace(
|
||||
key,
|
||||
registerImageEmoji(std::move(emoji), textColor)).first;
|
||||
}
|
||||
return i->second + InternalPadding(padding);
|
||||
}
|
||||
|
||||
QString CustomEmojiManager::registerImageEmoji(
|
||||
QImage emoji,
|
||||
bool textColor) {
|
||||
_internalEmoji.push_back({ std::move(emoji), textColor });
|
||||
return InternalPrefix() + QString::number(_internalEmoji.size() - 1);
|
||||
}
|
||||
|
||||
QString CustomEmojiManager::registerInternalEmoji(
|
||||
const style::icon &icon,
|
||||
QMargins padding,
|
||||
bool textColor) {
|
||||
const auto i = _iconEmoji.find(&icon);
|
||||
if (i != end(_iconEmoji)) {
|
||||
return i->second + InternalPadding(padding);
|
||||
}
|
||||
auto image = QImage(
|
||||
icon.size() * style::DevicePixelRatio(),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
image.fill(Qt::transparent);
|
||||
image.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
auto p = QPainter(&image);
|
||||
icon.paint(p, 0, 0, icon.width());
|
||||
p.end();
|
||||
|
||||
const auto result = registerImageEmoji(std::move(image), textColor);
|
||||
_iconEmoji.emplace(&icon, result);
|
||||
return result + InternalPadding(padding);
|
||||
}
|
||||
|
||||
[[nodiscard]] QString CustomEmojiManager::peerUserpicEmojiData(
|
||||
not_null<PeerData*> peer,
|
||||
QMargins padding,
|
||||
|
||||
@@ -83,16 +83,6 @@ public:
|
||||
[[nodiscard]] Main::Session &session() const;
|
||||
[[nodiscard]] Session &owner() const;
|
||||
|
||||
[[nodiscard]] QString registerInternalEmoji(
|
||||
const QString &key,
|
||||
QImage emoji,
|
||||
QMargins padding = {},
|
||||
bool textColor = true);
|
||||
[[nodiscard]] QString registerInternalEmoji(
|
||||
const style::icon &icon,
|
||||
QMargins padding = {},
|
||||
bool textColor = true);
|
||||
|
||||
[[nodiscard]] QString peerUserpicEmojiData(
|
||||
not_null<PeerData*> peer,
|
||||
QMargins padding = {},
|
||||
@@ -100,9 +90,6 @@ public:
|
||||
|
||||
[[nodiscard]] uint64 coloredSetId() const;
|
||||
|
||||
[[nodiscard]] TextWithEntities creditsEmoji(QMargins padding = {});
|
||||
[[nodiscard]] TextWithEntities ministarEmoji(QMargins padding = {});
|
||||
|
||||
private:
|
||||
static constexpr auto kSizeCount = int(SizeTag::kCount);
|
||||
|
||||
@@ -128,7 +115,6 @@ private:
|
||||
DocumentId documentId,
|
||||
SizeTag tag,
|
||||
int sizeOverride = 0);
|
||||
[[nodiscard]] QString registerImageEmoji(QImage emoji, bool textColor);
|
||||
|
||||
void request();
|
||||
void requestFinished();
|
||||
@@ -154,8 +140,6 @@ private:
|
||||
SizeTag tag,
|
||||
int sizeOverride,
|
||||
LoaderFactory factory);
|
||||
[[nodiscard]] std::unique_ptr<Ui::Text::CustomEmoji> internal(
|
||||
QStringView data);
|
||||
[[nodiscard]] std::unique_ptr<Ui::Text::CustomEmoji> userpic(
|
||||
QStringView data,
|
||||
Fn<void()> update,
|
||||
@@ -192,11 +176,6 @@ private:
|
||||
bool _repaintTimerScheduled = false;
|
||||
bool _requestSetsScheduled = false;
|
||||
|
||||
std::vector<InternalEmojiData> _internalEmoji;
|
||||
base::flat_map<not_null<const style::icon*>, QString> _iconEmoji;
|
||||
|
||||
base::flat_map<QString, QString> _imageEmoji;
|
||||
|
||||
#if 0 // inject-to-on_main
|
||||
crl::time _repaintsLastAdded = 0;
|
||||
rpl::lifetime _repaintsLifetime;
|
||||
|
||||
@@ -556,7 +556,10 @@ dialogsMiniForward: DialogsMiniIcon {
|
||||
skipMedia: 2px;
|
||||
}
|
||||
|
||||
dialogsMiniReplyIcon: icon {{ "mini_forward-flip_horizontal", attentionButtonFg, point(0px, 2px) }};
|
||||
dialogsMiniReplyIcon: IconEmoji {
|
||||
icon: icon {{ "mini_forward-flip_horizontal", attentionButtonFg }};
|
||||
padding: margins(0px, 2px, 0px, 0px);
|
||||
}
|
||||
|
||||
dialogsMiniReplyStory: DialogsMiniIcon {
|
||||
icon: ThreeStateIcon {
|
||||
@@ -799,8 +802,10 @@ dialogsSearchTagSkip: point(8px, 4px);
|
||||
dialogsSearchTagBottom: 10px;
|
||||
dialogsSearchTagLocked: icon{{ "dialogs/mini_tag_lock", lightButtonFgOver }};
|
||||
dialogsSearchTagPromo: defaultTextStyle;
|
||||
dialogsSearchTagArrow: icon{{ "dialogs/mini_arrow", windowSubTextFg }};
|
||||
dialogsSearchTagArrowPadding: margins(-6px, 3px, 0px, 0px);
|
||||
dialogsSearchTagArrow: IconEmoji {
|
||||
icon: icon{{ "dialogs/mini_arrow", windowSubTextFg }};
|
||||
padding: margins(-6px, 3px, 0px, 0px);
|
||||
}
|
||||
dialogsSearchTagPromoLeft: 6px;
|
||||
dialogsSearchTagPromoRight: 1px;
|
||||
dialogsSearchTagPromoSkip: 6px;
|
||||
@@ -833,6 +838,8 @@ dialogsTopBarSuggestionAboutStyle: TextStyle(defaultTextStyle) {
|
||||
font: font(11px);
|
||||
}
|
||||
|
||||
dialogsMiniQuoteIcon: icon{{ "chat/mini_quote", dialogsTextFg }};
|
||||
|
||||
postsSearchIntroTitle: FlatLabel(defaultFlatLabel) {
|
||||
textFg: windowBoldFg;
|
||||
minWidth: 64px;
|
||||
@@ -863,7 +870,6 @@ postsSearchIntroFooter: FlatLabel(defaultFlatLabel) {
|
||||
}
|
||||
}
|
||||
postsSearchIntroFooterMargin: margins(20px, 12px, 20px, 0px);
|
||||
|
||||
postsSearchIcon: icon {{ "box_search", windowFgActive }};
|
||||
postsSearchIconPadding: margins(12px, 1px, 6px, 0px);
|
||||
postsSearchArrow: icon {{ "dialogs/mini_arrow", windowFgActive }};
|
||||
|
||||
@@ -241,7 +241,7 @@ uint64 Entry::computeSortPosition(FilterId filterId) const {
|
||||
|
||||
void Entry::updateChatListExistence() {
|
||||
if (const auto history = asHistory()) {
|
||||
if (const auto channel = history->peer->asMonoforum()) {
|
||||
if (history->peer->asMonoforum()) {
|
||||
if (!folderKnown()) {
|
||||
history->clearFolder();
|
||||
}
|
||||
|
||||
@@ -1241,6 +1241,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||
auto to = ceilclamp(r.y() + r.height() - skip, _st->height, 0, _previewResults.size());
|
||||
p.translate(0, from * _st->height);
|
||||
if (from < _previewResults.size()) {
|
||||
const auto searchLowerText = _searchState.query.toLower();
|
||||
for (; from < to; ++from) {
|
||||
const auto &result = _previewResults[from];
|
||||
const auto active = isSearchResultActive(result.get(), activeEntry);
|
||||
@@ -1258,6 +1259,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||
.currentBg = currentBg(),
|
||||
.filter = _filterId,
|
||||
.now = ms,
|
||||
.searchLowerText = QStringView(searchLowerText),
|
||||
.width = fullWidth,
|
||||
.active = active,
|
||||
.selected = selected,
|
||||
@@ -1285,6 +1287,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||
tr::now,
|
||||
lt_count,
|
||||
_searchedMigratedCount + _searchedCount);
|
||||
const auto searchLowerText = _searchState.query.toLower();
|
||||
p.fillRect(0, 0, fullWidth, st::searchedBarHeight, st::searchedBarBg);
|
||||
p.setFont(st::searchedBarFont);
|
||||
p.setPen(st::searchedBarFg);
|
||||
@@ -1332,6 +1335,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||
.currentBg = currentBg(),
|
||||
.filter = _filterId,
|
||||
.now = ms,
|
||||
.searchLowerText = QStringView(searchLowerText),
|
||||
.width = fullWidth,
|
||||
.active = active,
|
||||
.selected = selected,
|
||||
@@ -4151,14 +4155,14 @@ void InnerWidget::refreshEmpty() {
|
||||
.name = u"no_chats"_q,
|
||||
.sizeOverride = Size(st::changePhoneIconSize),
|
||||
});
|
||||
_emptyList->add(
|
||||
object_ptr<Ui::CenterWrap<>>(_emptyList, std::move(icon.widget)));
|
||||
_emptyList->add(std::move(icon.widget), style::al_top);
|
||||
Ui::AddSkip(_emptyList);
|
||||
_emptyList->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
_emptyList,
|
||||
tr::lng_no_conversations(),
|
||||
st::dialogEmptyButtonLabel));
|
||||
st::dialogEmptyButtonLabel),
|
||||
style::al_top);
|
||||
if (_state == WidgetState::Default) {
|
||||
icon.animate(anim::repeat::once);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,12 @@ namespace {
|
||||
constexpr auto kQueryDelay = crl::time(500);
|
||||
constexpr auto kPerPage = 50;
|
||||
|
||||
[[nodiscard]] const QRegularExpression &SearchSplitter() {
|
||||
static const auto result = QRegularExpression(QString::fromLatin1(""
|
||||
"[\\s\\-\\+\\(\\)\\[\\]\\{\\}\\<\\>\\,\\.\\!\\_\\;\\\"\\'\\x0]"));
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PostsSearch::PostsSearch(not_null<Main::Session*> session)
|
||||
@@ -58,7 +64,9 @@ void PostsSearch::requestMore() {
|
||||
}
|
||||
|
||||
void PostsSearch::setQuery(const QString &query) {
|
||||
const auto words = TextUtilities::PrepareSearchWords(query);
|
||||
const auto words = TextUtilities::PrepareSearchWords(
|
||||
query,
|
||||
&SearchSplitter());
|
||||
const auto prepared = words.isEmpty() ? QString() : words.join(' ');
|
||||
if (_queryExact == query) {
|
||||
return;
|
||||
@@ -109,7 +117,9 @@ void PostsSearch::pushStateUpdate(const Entry &entry) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!entry.pages.empty() || entry.loaded) {
|
||||
const auto empty = entry.pages.empty()
|
||||
|| (entry.pages.size() == 1 && entry.pages.front().empty());
|
||||
if (!empty || (entry.loaded && !_query->isEmpty())) {
|
||||
if (!entry.pages.empty()) {
|
||||
++entry.pagesPushed;
|
||||
}
|
||||
|
||||
@@ -52,24 +52,14 @@ namespace {
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] Ui::Text::String FillAdditionalText(
|
||||
not_null<Data::Session*> owner,
|
||||
int width) {
|
||||
auto emoji = Ui::Text::SingleCustomEmoji(
|
||||
owner->customEmojiManager().registerInternalEmoji(
|
||||
st::dialogsSearchTagArrow,
|
||||
st::dialogsSearchTagArrowPadding));
|
||||
[[nodiscard]] Ui::Text::String FillAdditionalText(int width) {
|
||||
auto emoji = Ui::Text::IconEmoji(&st::dialogsSearchTagArrow);
|
||||
auto result = Ui::Text::String();
|
||||
const auto context = Core::TextContext({
|
||||
.session = &owner->session(),
|
||||
.customEmojiLoopLimit = 1,
|
||||
});
|
||||
const auto attempt = [&](const auto &phrase) {
|
||||
result.setMarkedText(
|
||||
st::dialogsSearchTagPromo,
|
||||
phrase(tr::now, lt_arrow, emoji, Ui::Text::WithEntities),
|
||||
kMarkupTextOptions,
|
||||
context);
|
||||
kMarkupTextOptions);
|
||||
return result.maxWidth() < width;
|
||||
};
|
||||
if (attempt(tr::lng_add_tag_phrase_long)
|
||||
@@ -230,7 +220,7 @@ void SearchTags::layout() {
|
||||
if (_tags.size() == 1 && _tags.front().promo) {
|
||||
_additionalLeft = x - skip.x() + st::dialogsSearchTagPromoSkip;
|
||||
const auto additionalWidth = _width - _additionalLeft;
|
||||
_additionalText = FillAdditionalText(_owner, additionalWidth);
|
||||
_additionalText = FillAdditionalText(additionalWidth);
|
||||
} else {
|
||||
_additionalText = {};
|
||||
}
|
||||
|
||||
@@ -2757,6 +2757,11 @@ void Widget::showMainMenu() {
|
||||
}
|
||||
|
||||
void Widget::searchMessages(SearchState state) {
|
||||
if (const auto peer = state.inChat.peer()) {
|
||||
if (_openedForum && peer->forum() != _openedForum) {
|
||||
controller()->closeForum();
|
||||
}
|
||||
}
|
||||
applySearchState(std::move(state));
|
||||
session().local().saveRecentSearchHashtags(_searchState.query);
|
||||
}
|
||||
|
||||
@@ -602,13 +602,9 @@ void PaintRow(
|
||||
}),
|
||||
Text::WithEntities);
|
||||
if (draft && draft->reply) {
|
||||
auto &data = thread->owner().customEmojiManager();
|
||||
draftText = Ui::Text::Colorized(
|
||||
Ui::Text::SingleCustomEmoji(
|
||||
data.registerInternalEmoji(
|
||||
st::dialogsMiniReplyIcon,
|
||||
{},
|
||||
true))).append(std::move(draftText));
|
||||
Ui::Text::IconEmoji(&st::dialogsMiniReplyIcon)
|
||||
).append(std::move(draftText));
|
||||
}
|
||||
const auto context = Core::TextContext({
|
||||
.session = &thread->session(),
|
||||
@@ -1163,6 +1159,7 @@ void RowPainter::Paint(
|
||||
return {};
|
||||
}();
|
||||
previewOptions.ignoreGroup = true;
|
||||
previewOptions.searchLowerText = context.searchLowerText;
|
||||
|
||||
const auto badgesState = context.displayUnreadInfo
|
||||
? entry->chatListBadgesState()
|
||||
|
||||
@@ -64,6 +64,7 @@ struct PaintContext {
|
||||
FilterId filter = 0;
|
||||
float64 topicsExpanded = 0.;
|
||||
crl::time now = 0;
|
||||
QStringView searchLowerText;
|
||||
int width = 0;
|
||||
bool active = false;
|
||||
bool selected = false;
|
||||
|
||||
@@ -9,15 +9,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "history/view/history_view_item_preview.h"
|
||||
#include "main/main_session.h"
|
||||
#include "dialogs/dialogs_three_state_icon.h"
|
||||
#include "dialogs/ui/dialogs_layout.h"
|
||||
#include "dialogs/ui/dialogs_topics_view.h"
|
||||
#include "ui/effects/spoiler_mess.h"
|
||||
#include "ui/text/custom_emoji_helper.h"
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/power_saving.h"
|
||||
#include "core/ui_integration.h"
|
||||
#include "lang/lang_keys.h"
|
||||
@@ -186,7 +189,7 @@ void MessageView::prepare(
|
||||
: nullptr;
|
||||
const auto hasImages = !preview.images.empty();
|
||||
const auto history = item->history();
|
||||
const auto context = Core::TextContext({
|
||||
auto context = Core::TextContext({
|
||||
.session = &history->session(),
|
||||
.repaint = customEmojiRepaint,
|
||||
.customEmojiLoopLimit = kEmojiLoopCount,
|
||||
@@ -207,13 +210,105 @@ void MessageView::prepare(
|
||||
}
|
||||
TextUtilities::Trim(preview.text);
|
||||
auto textToCache = DialogsPreviewText(std::move(preview.text));
|
||||
|
||||
if (!options.searchLowerText.isEmpty()) {
|
||||
static constexpr auto kLeftShift = 15;
|
||||
auto minFrom = std::numeric_limits<uint16>::max();
|
||||
|
||||
const auto words = Ui::Text::Words(options.searchLowerText);
|
||||
textToCache.entities.reserve(textToCache.entities.size()
|
||||
+ words.size());
|
||||
|
||||
for (const auto &word : words) {
|
||||
const auto selection = HistoryView::FindSearchQueryHighlight(
|
||||
textToCache.text,
|
||||
word);
|
||||
if (!selection.empty()) {
|
||||
minFrom = std::min(minFrom, selection.from);
|
||||
textToCache.entities.push_back(EntityInText{
|
||||
EntityType::Colorized,
|
||||
selection.from,
|
||||
selection.to - selection.from
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (minFrom == std::numeric_limits<uint16>::max()
|
||||
&& !item->replyTo().quote.empty()) {
|
||||
auto textQuote = TextWithEntities();
|
||||
for (const auto &word : words) {
|
||||
const auto selection = HistoryView::FindSearchQueryHighlight(
|
||||
item->replyTo().quote.text,
|
||||
word);
|
||||
if (!selection.empty()) {
|
||||
minFrom = 0;
|
||||
if (textQuote.empty()) {
|
||||
textQuote = item->replyTo().quote;
|
||||
}
|
||||
textQuote.entities.push_back(EntityInText{
|
||||
EntityType::Colorized,
|
||||
selection.from,
|
||||
selection.to - selection.from
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!textQuote.empty()) {
|
||||
auto helper = Ui::Text::CustomEmojiHelper(context);
|
||||
const auto factory = Ui::Text::PaletteDependentEmoji{
|
||||
.factory = [=] {
|
||||
const auto &icon = st::dialogsMiniQuoteIcon;
|
||||
auto image = QImage(
|
||||
icon.size() * style::DevicePixelRatio(),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
image.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
image.fill(Qt::transparent);
|
||||
{
|
||||
auto p = Painter(&image);
|
||||
icon.paintInCenter(
|
||||
p,
|
||||
Rect(icon.size()),
|
||||
st::dialogsTextFg->c);
|
||||
}
|
||||
return image;
|
||||
},
|
||||
.margin = QMargins(
|
||||
st::lineWidth * 2,
|
||||
0,
|
||||
st::lineWidth * 2,
|
||||
0),
|
||||
};
|
||||
textToCache = textQuote
|
||||
.append(helper.paletteDependent(factory))
|
||||
.append(std::move(textToCache));
|
||||
context = helper.context(customEmojiRepaint);
|
||||
}
|
||||
}
|
||||
|
||||
if (!words.empty() && minFrom != std::numeric_limits<uint16>::max()) {
|
||||
std::sort(
|
||||
textToCache.entities.begin(),
|
||||
textToCache.entities.end(),
|
||||
[](const auto &a, const auto &b) {
|
||||
return a.offset() < b.offset();
|
||||
});
|
||||
|
||||
const auto textSize = textToCache.text.size();
|
||||
minFrom = (minFrom > textSize || minFrom < kLeftShift)
|
||||
? 0
|
||||
: minFrom - kLeftShift;
|
||||
|
||||
textToCache = TextWithEntities(
|
||||
minFrom > 0 ? kQEllipsis : QString())
|
||||
.append(Text::Mid(std::move(textToCache), minFrom));
|
||||
}
|
||||
}
|
||||
_hasPlainLinkAtBegin = !textToCache.entities.empty()
|
||||
&& (textToCache.entities.front().type() == EntityType::Colorized);
|
||||
_textCache.setMarkedText(
|
||||
st::dialogsTextStyle,
|
||||
std::move(textToCache),
|
||||
DialogTextOptions(),
|
||||
context);
|
||||
std::move(context));
|
||||
_textCachedFor = item;
|
||||
_imagesCache = std::move(preview.images);
|
||||
if (!ranges::any_of(_imagesCache, &ItemPreviewImage::hasSpoiler)) {
|
||||
|
||||
@@ -49,6 +49,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/controls/swipe_handler.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/text/custom_emoji_helper.h"
|
||||
#include "ui/text/custom_emoji_text_badge.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/menu/menu_add_action_callback_factory.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
@@ -157,34 +159,6 @@ struct EntryMenuDescriptor {
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] QImage MakeNewBadgeImage() {
|
||||
auto text = Ui::Text::String(
|
||||
st::settingsPremiumNewBadge.style,
|
||||
tr::lng_premium_summary_new_badge(tr::now));
|
||||
const auto size = QSize(text.maxWidth(), text.minHeight());
|
||||
const auto padding = st::settingsPremiumNewBadgePadding;
|
||||
const auto full = size.grownBy(padding);
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
|
||||
auto result = QImage(full * ratio, QImage::Format_ARGB32_Premultiplied);
|
||||
result.setDevicePixelRatio(ratio);
|
||||
result.fill(Qt::transparent);
|
||||
|
||||
auto p = QPainter(&result);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::windowBgActive);
|
||||
|
||||
const auto r = padding.left();
|
||||
p.drawRoundedRect(0, 0, full.width(), full.height(), r, r);
|
||||
|
||||
p.setPen(st::windowFgActive);
|
||||
text.draw(p, { .position = { padding.left(), padding.top() } });
|
||||
|
||||
p.end();
|
||||
return result;
|
||||
}
|
||||
|
||||
void FillEntryMenu(
|
||||
const Ui::Menu::MenuCallback &add,
|
||||
EntryMenuDescriptor &&descriptor) {
|
||||
@@ -1502,25 +1476,20 @@ void Suggestions::setupTabs() {
|
||||
},
|
||||
};
|
||||
|
||||
const auto manager = &_controller->session().data().customEmojiManager();
|
||||
const auto badgeData = manager->registerInternalEmoji(
|
||||
u"posts_search_new_badge"_q,
|
||||
MakeNewBadgeImage(),
|
||||
st::badgeEmojiMargin,
|
||||
false);
|
||||
auto helper = Ui::Text::CustomEmojiHelper();
|
||||
auto sections = std::vector<TextWithEntities>();
|
||||
for (const auto key : _tabKeys) {
|
||||
const auto i = labels.find(key);
|
||||
Assert(i != end(labels));
|
||||
auto text = TextWithEntities{ i->second };
|
||||
if (key.tab == Tab::Posts) {
|
||||
text.append(' ').append(Ui::Text::SingleCustomEmoji(badgeData));
|
||||
text.append(' ').append(helper.paletteDependent(
|
||||
Ui::Text::CustomEmojiTextBadge(
|
||||
tr::lng_premium_summary_new_badge(tr::now))));
|
||||
}
|
||||
sections.push_back(std::move(text));
|
||||
}
|
||||
_tabs->setSections(sections, Core::TextContext({
|
||||
.session = &_controller->session(),
|
||||
}));
|
||||
_tabs->setSections(sections, helper.context());
|
||||
_tabs->sectionActivated(
|
||||
) | rpl::start_with_next([=](int section) {
|
||||
Assert(section >= 0 && section < _tabKeys.size());
|
||||
@@ -1966,6 +1935,7 @@ void Suggestions::setupPostsSearch() {
|
||||
nullptr,
|
||||
{ .posts = true, .start = true },
|
||||
state.totalCount);
|
||||
_postsScroll->scrollToY(0);
|
||||
updatePostsSearchVisibleRange();
|
||||
}
|
||||
}, _postsWrap->lifetime());
|
||||
@@ -2100,6 +2070,10 @@ void Suggestions::setupPostsIntro(const PostsSearchIntroState &intro) {
|
||||
}
|
||||
}, _postsSearchIntro->lifetime());
|
||||
|
||||
_postsScroll->heightValue() | rpl::start_with_next([=](int height) {
|
||||
_postsWrap->resize(_postsWrap->width(), height);
|
||||
}, _postsSearchIntro->lifetime());
|
||||
|
||||
_postsSearchIntro->show();
|
||||
updateControlsGeometry();
|
||||
}
|
||||
|
||||
@@ -170,34 +170,41 @@ void PostsSearchIntro::setup() {
|
||||
_content.get(),
|
||||
std::move(title),
|
||||
st::postsSearchIntroTitle),
|
||||
st::postsSearchIntroTitleMargin);
|
||||
st::postsSearchIntroTitleMargin,
|
||||
style::al_top);
|
||||
_title->setTryMakeSimilarLines(true);
|
||||
_subtitle = _content->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
_content.get(),
|
||||
std::move(subtitle),
|
||||
st::postsSearchIntroSubtitle),
|
||||
st::postsSearchIntroSubtitleMargin);
|
||||
st::postsSearchIntroSubtitleMargin,
|
||||
style::al_top);
|
||||
_subtitle->setTryMakeSimilarLines(true);
|
||||
_button = _content->add(
|
||||
object_ptr<Ui::CenterWrap<Ui::RoundButton>>(
|
||||
object_ptr<Ui::RoundButton>(
|
||||
_content.get(),
|
||||
object_ptr<Ui::RoundButton>(
|
||||
_content.get(),
|
||||
rpl::single(QString()),
|
||||
st::postsSearchIntroButton))
|
||||
)->entity();
|
||||
rpl::single(QString()),
|
||||
st::postsSearchIntroButton),
|
||||
style::al_top);
|
||||
_button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
|
||||
_footer = _content->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
_content.get(),
|
||||
std::move(footer),
|
||||
st::postsSearchIntroFooter),
|
||||
st::postsSearchIntroFooterMargin);
|
||||
st::postsSearchIntroFooterMargin,
|
||||
style::al_top);
|
||||
_footer->setTryMakeSimilarLines(true);
|
||||
|
||||
_state.value(
|
||||
) | rpl::start_with_next([=](const PostsSearchIntroState &state) {
|
||||
if (state.query.trimmed().isEmpty()) {
|
||||
_button->resize(_button->width(), 0);
|
||||
_content->resizeToWidth(width());
|
||||
return;
|
||||
}
|
||||
|
||||
auto copy = _button->children();
|
||||
for (const auto child : copy) {
|
||||
delete child;
|
||||
@@ -232,6 +239,7 @@ void PostsSearchIntro::setup() {
|
||||
st::resaleButtonTitle,
|
||||
st::resaleButtonSubtitle);
|
||||
}
|
||||
_button->resize(_button->width(), st::postsSearchIntroButton.height);
|
||||
_content->resizeToWidth(width());
|
||||
}, _button->lifetime());
|
||||
}
|
||||
|
||||
@@ -2068,7 +2068,7 @@ bool ApiWrap::processFileLoad(
|
||||
} else if (!story && (_settings->media.types & type) != type) {
|
||||
file.skipReason = SkipReason::FileType;
|
||||
return true;
|
||||
} else if (!story && fullSize >= _settings->media.sizeLimit) {
|
||||
} else if (!story && fullSize > _settings->media.sizeLimit) {
|
||||
// Don't load thumbs for large files that we skip.
|
||||
file.skipReason = SkipReason::FileSize;
|
||||
return true;
|
||||
|
||||
@@ -1009,7 +1009,7 @@ void InnerWidget::itemsAdded(Direction direction, int addedCount) {
|
||||
}
|
||||
|
||||
void InnerWidget::updateSize() {
|
||||
TWidget::resizeToWidth(width());
|
||||
RpWidget::resizeToWidth(width());
|
||||
restoreScrollPosition();
|
||||
updateVisibleTopItem();
|
||||
checkPreloadMore();
|
||||
@@ -1688,7 +1688,7 @@ void InnerWidget::mouseReleaseEvent(QMouseEvent *e) {
|
||||
|
||||
void InnerWidget::enterEventHook(QEnterEvent *e) {
|
||||
mouseActionUpdate(QCursor::pos());
|
||||
return TWidget::enterEventHook(e);
|
||||
return RpWidget::enterEventHook(e);
|
||||
}
|
||||
|
||||
void InnerWidget::leaveEventHook(QEvent *e) {
|
||||
@@ -1702,7 +1702,7 @@ void InnerWidget::leaveEventHook(QEvent *e) {
|
||||
_cursor = style::cur_default;
|
||||
setCursor(_cursor);
|
||||
}
|
||||
return TWidget::leaveEventHook(e);
|
||||
return RpWidget::leaveEventHook(e);
|
||||
}
|
||||
|
||||
void InnerWidget::mouseActionStart(const QPoint &screenPos, Qt::MouseButton button) {
|
||||
|
||||
@@ -77,7 +77,7 @@ public:
|
||||
|
||||
void resizeToWidth(int newWidth, int minHeight) {
|
||||
_minHeight = minHeight;
|
||||
return TWidget::resizeToWidth(newWidth);
|
||||
return RpWidget::resizeToWidth(newWidth);
|
||||
}
|
||||
|
||||
void saveState(not_null<SectionMemento*> memento);
|
||||
|
||||
@@ -37,7 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace AdminLog {
|
||||
|
||||
class FixedBar final : public TWidget {
|
||||
class FixedBar final : public Ui::RpWidget {
|
||||
public:
|
||||
FixedBar(
|
||||
QWidget *parent,
|
||||
@@ -109,7 +109,8 @@ object_ptr<Window::SectionWidget> SectionMemento::createWidget(
|
||||
FixedBar::FixedBar(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<ChannelData*> channel) : TWidget(parent)
|
||||
not_null<ChannelData*> channel)
|
||||
: RpWidget(parent)
|
||||
, _controller(controller)
|
||||
, _channel(channel)
|
||||
, _field(this, st::defaultMultiSelectSearchField, tr::lng_dlg_filter())
|
||||
@@ -276,7 +277,7 @@ void FixedBar::mousePressEvent(QMouseEvent *e) {
|
||||
if (e->button() == Qt::LeftButton) {
|
||||
goBack();
|
||||
} else {
|
||||
TWidget::mousePressEvent(e);
|
||||
RpWidget::mousePressEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_channel_admins.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_chat_filters.h"
|
||||
#include "data/data_replies_list.h"
|
||||
#include "data/data_send_action.h"
|
||||
#include "data/data_star_gift.h"
|
||||
#include "data/data_emoji_statuses.h"
|
||||
@@ -3170,6 +3171,58 @@ void History::applyDialogTopMessage(MsgId topMessageId) {
|
||||
}
|
||||
}
|
||||
|
||||
void History::tryMarkForumIntervalRead(
|
||||
MsgId wasInboxReadBefore,
|
||||
MsgId nowInboxReadBefore) {
|
||||
if (!isForum()
|
||||
|| !peer->asChannel()->useSubsectionTabs()
|
||||
|| (nowInboxReadBefore <= wasInboxReadBefore)) {
|
||||
return;
|
||||
} else if (loadedAtBottom() && nowInboxReadBefore >= minMsgId()) {
|
||||
// Count for each sublist how many messages are still not read.
|
||||
auto counts = base::flat_map<not_null<Data::ForumTopic*>, int>();
|
||||
for (const auto &block : blocks) {
|
||||
for (const auto &message : block->messages) {
|
||||
const auto item = message->data();
|
||||
if (!item->isRegular() || item->id < nowInboxReadBefore) {
|
||||
continue;
|
||||
}
|
||||
if (const auto topic = item->topic()) {
|
||||
++counts[topic];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (const auto forum = peer->forum()) {
|
||||
forum->updateUnreadCounts(nowInboxReadBefore - 1, counts);
|
||||
}
|
||||
} else if (minMsgId() <= wasInboxReadBefore
|
||||
&& maxMsgId() >= nowInboxReadBefore) {
|
||||
// Count for each sublist how many messages were read.
|
||||
for (const auto &block : blocks) {
|
||||
for (const auto &message : block->messages) {
|
||||
const auto item = message->data();
|
||||
if (!item->isRegular() || item->id < wasInboxReadBefore) {
|
||||
continue;
|
||||
} else if (item->id >= nowInboxReadBefore) {
|
||||
break;
|
||||
}
|
||||
if (const auto topic = item->topic()) {
|
||||
const auto replies = topic->replies();
|
||||
const auto unread = replies->unreadCountCurrent();
|
||||
if (unread > 0) {
|
||||
replies->setInboxReadTill(item->id, unread - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We can't invalidate sublist unread counts here, because no read
|
||||
// request was yet sent to the server (so it can't return correct
|
||||
// values yet), we need to do that after we send read request.
|
||||
_flags |= Flag::MonoAndForumUnreadInvalidatePending;
|
||||
}
|
||||
}
|
||||
|
||||
void History::tryMarkMonoforumIntervalRead(
|
||||
MsgId wasInboxReadBefore,
|
||||
MsgId nowInboxReadBefore) {
|
||||
@@ -3215,24 +3268,29 @@ void History::tryMarkMonoforumIntervalRead(
|
||||
// We can't invalidate sublist unread counts here, because no read
|
||||
// request was yet sent to the server (so it can't return correct
|
||||
// values yet), we need to do that after we send read request.
|
||||
_flags |= Flag::MonoforumUnreadInvalidatePending;
|
||||
_flags |= Flag::MonoAndForumUnreadInvalidatePending;
|
||||
}
|
||||
}
|
||||
|
||||
void History::validateMonoforumUnread(MsgId readTillId) {
|
||||
if (!(_flags & Flag::MonoforumUnreadInvalidatePending)) {
|
||||
void History::validateMonoAndForumUnread(MsgId readTillId) {
|
||||
if (!(_flags & Flag::MonoAndForumUnreadInvalidatePending)) {
|
||||
return;
|
||||
}
|
||||
_flags &= ~Flag::MonoforumUnreadInvalidatePending;
|
||||
if (!amMonoforumAdmin()) {
|
||||
return;
|
||||
} else if (const auto monoforum = peer->monoforum()) {
|
||||
monoforum->markUnreadCountsUnknown(readTillId);
|
||||
_flags &= ~Flag::MonoAndForumUnreadInvalidatePending;
|
||||
if (isForum()) {
|
||||
if (const auto forum = peer->forum()) {
|
||||
forum->markUnreadCountsUnknown(readTillId);
|
||||
}
|
||||
} else if (amMonoforumAdmin()) {
|
||||
if (const auto monoforum = peer->monoforum()) {
|
||||
monoforum->markUnreadCountsUnknown(readTillId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void History::setInboxReadTill(MsgId upTo) {
|
||||
if (_inboxReadBefore) {
|
||||
tryMarkForumIntervalRead(*_inboxReadBefore, upTo + 1);
|
||||
tryMarkMonoforumIntervalRead(*_inboxReadBefore, upTo + 1);
|
||||
accumulate_max(*_inboxReadBefore, upTo + 1);
|
||||
} else {
|
||||
@@ -3420,6 +3478,26 @@ bool History::suggestDraftAllowed() const {
|
||||
return peer->isMonoforum() && !peer->amMonoforumAdmin();
|
||||
}
|
||||
|
||||
bool History::hasForumThreadBars() const {
|
||||
if (amMonoforumAdmin()) {
|
||||
return true;
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
return channel->forum() && channel->useSubsectionTabs();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void History::forumTabsChanged(bool forumTabs) {
|
||||
for (auto &block : blocks) {
|
||||
for (auto &view : block->messages) {
|
||||
view->setPendingResize();
|
||||
if (forumTabs || view->Has<HistoryView::ForumThreadBar>()) {
|
||||
view->previousInBlocksChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
not_null<History*> History::migrateToOrMe() const {
|
||||
if (const auto to = peer->migrateTo()) {
|
||||
return owner().history(to);
|
||||
|
||||
@@ -80,6 +80,8 @@ public:
|
||||
void monoforumChanged(Data::SavedMessages *old);
|
||||
[[nodiscard]] bool amMonoforumAdmin() const;
|
||||
[[nodiscard]] bool suggestDraftAllowed() const;
|
||||
[[nodiscard]] bool hasForumThreadBars() const;
|
||||
void forumTabsChanged(bool forumTabs);
|
||||
|
||||
[[nodiscard]] not_null<History*> migrateToOrMe() const;
|
||||
[[nodiscard]] History *migrateFrom() const;
|
||||
@@ -434,7 +436,10 @@ public:
|
||||
void tryMarkMonoforumIntervalRead(
|
||||
MsgId wasInboxReadBefore,
|
||||
MsgId nowInboxReadBefore);
|
||||
void validateMonoforumUnread(MsgId readTillId);
|
||||
void tryMarkForumIntervalRead(
|
||||
MsgId wasInboxReadBefore,
|
||||
MsgId nowInboxReadBefore);
|
||||
void validateMonoAndForumUnread(MsgId readTillId);
|
||||
|
||||
[[nodiscard]] bool isTopPromoted() const;
|
||||
|
||||
@@ -480,7 +485,7 @@ private:
|
||||
FakeUnreadWhileOpened = (1 << 5),
|
||||
HasPinnedMessages = (1 << 6),
|
||||
ResolveChatListMessage = (1 << 7),
|
||||
MonoforumUnreadInvalidatePending = (1 << 8),
|
||||
MonoAndForumUnreadInvalidatePending = (1 << 8),
|
||||
};
|
||||
using Flags = base::flags<Flag>;
|
||||
friend inline constexpr auto is_flag_type(Flag) {
|
||||
|
||||
@@ -77,6 +77,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_session_settings.h"
|
||||
#include "mainwidget.h"
|
||||
#include "menu/menu_item_download_files.h"
|
||||
#include "menu/menu_item_rate_transcribe.h"
|
||||
#include "menu/menu_item_rate_transcribe_session.h"
|
||||
#include "menu/menu_sponsored.h"
|
||||
#include "core/application.h"
|
||||
#include "apiwrap.h"
|
||||
@@ -934,8 +936,8 @@ void HistoryInner::enumerateDates(Method method) {
|
||||
}
|
||||
|
||||
template <typename Method>
|
||||
void HistoryInner::enumerateMonoforumSenders(Method method) {
|
||||
if (!_history->amMonoforumAdmin()) {
|
||||
void HistoryInner::enumerateForumThreadBars(Method method) {
|
||||
if (!_history->hasForumThreadBars()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -952,28 +954,28 @@ void HistoryInner::enumerateMonoforumSenders(Method method) {
|
||||
// -1 means we didn't find a same-day with previous message yet.
|
||||
auto lowestInOneBunchItemBottom = -1;
|
||||
|
||||
auto senderCallback = [&](not_null<Element*> view, int itemtop, int itembottom) {
|
||||
auto barCallback = [&](not_null<Element*> view, int itemtop, int itembottom) {
|
||||
const auto item = view->data();
|
||||
if (lowestInOneBunchItemBottom < 0 && view->isInOneBunchWithPrevious()) {
|
||||
lowestInOneBunchItemBottom = itembottom - view->marginBottom();
|
||||
}
|
||||
|
||||
// Call method on a sender for all messages that have it and for those who are not showing it
|
||||
// Call method on a bar for all messages that have it and for those who are not showing it
|
||||
// because they are in a one day together with the previous message if they are top-most visible.
|
||||
if (view->displayMonoforumSender() || (!item->isEmpty() && itemtop <= _visibleAreaTop)) {
|
||||
if (view->displayForumThreadBar() || (!item->isEmpty() && itemtop <= _visibleAreaTop)) {
|
||||
if (lowestInOneBunchItemBottom < 0) {
|
||||
lowestInOneBunchItemBottom = itembottom - view->marginBottom();
|
||||
}
|
||||
// Attach sender to the top of the visible area with the same margin as it has in service message.
|
||||
int senderTop = qMax(itemtop + view->displayedDateHeight(), _visibleAreaTop + skip) + st::msgServiceMargin.top();
|
||||
// Attach bar to the top of the visible area with the same margin as it has in service message.
|
||||
int barTop = qMax(itemtop + view->displayedDateHeight(), _visibleAreaTop + skip) + st::msgServiceMargin.top();
|
||||
|
||||
// Do not let the sender go below the single-sender messages pack bottom line.
|
||||
int senderHeight = st::msgServicePadding.bottom() + st::msgServiceFont->height + st::msgServicePadding.top();
|
||||
senderTop = qMin(senderTop, lowestInOneBunchItemBottom - senderHeight);
|
||||
// Do not let the bar go below the single-bar messages pack bottom line.
|
||||
int barHeight = st::msgServicePadding.bottom() + st::msgServiceFont->height + st::msgServicePadding.top();
|
||||
barTop = qMin(barTop, lowestInOneBunchItemBottom - barHeight);
|
||||
|
||||
// Call the template callback function that was passed
|
||||
// and return if it finished everything it needed.
|
||||
if (!method(view, itemtop, senderTop)) {
|
||||
if (!method(view, itemtop, barTop)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -986,7 +988,7 @@ void HistoryInner::enumerateMonoforumSenders(Method method) {
|
||||
return true;
|
||||
};
|
||||
|
||||
enumerateItems<EnumItemsDirection::BottomToTop>(senderCallback);
|
||||
enumerateItems<EnumItemsDirection::BottomToTop>(barCallback);
|
||||
}
|
||||
|
||||
TextSelection HistoryInner::computeRenderSelection(
|
||||
@@ -1401,31 +1403,31 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
});
|
||||
p.setOpacity(1.);
|
||||
|
||||
enumerateMonoforumSenders([&](not_null<Element*> view, int itemtop, int senderTop) {
|
||||
// stop the enumeration if the sender is above the painted rect
|
||||
if (senderTop + dateHeight <= clip.top()) {
|
||||
enumerateForumThreadBars([&](not_null<Element*> view, int itemtop, int barTop) {
|
||||
// stop the enumeration if the bar is above the painted rect
|
||||
if (barTop + dateHeight <= clip.top()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto displaySender = view->displayMonoforumSender();
|
||||
auto senderInPlace = displaySender;
|
||||
if (senderInPlace) {
|
||||
const auto correctSenderTop = itemtop + view->displayedDateHeight() + st::msgServiceMargin.top();
|
||||
senderInPlace = (senderTop < correctSenderTop + st::msgServiceMargin.top());
|
||||
const auto displayBar = view->displayForumThreadBar();
|
||||
auto barInPlace = displayBar;
|
||||
if (barInPlace) {
|
||||
const auto correctBarTop = itemtop + view->displayedDateHeight() + st::msgServiceMargin.top();
|
||||
barInPlace = (barTop < correctBarTop + st::msgServiceMargin.top());
|
||||
}
|
||||
|
||||
// paint the sender if it intersects the painted rect
|
||||
if (senderTop < clip.top() + clip.height()) {
|
||||
const auto senderY = senderTop - st::msgServiceMargin.top();
|
||||
if (const auto sender = view->Get<HistoryView::MonoforumSenderBar>()) {
|
||||
sender->paint(p, context.st, senderY, _contentWidth, _isChatWide, !senderInPlace);
|
||||
// paint the bar if it intersects the painted rect
|
||||
if (barTop < clip.top() + clip.height()) {
|
||||
const auto barY = barTop - st::msgServiceMargin.top();
|
||||
if (const auto bar = view->Get<HistoryView::ForumThreadBar>()) {
|
||||
bar->paint(p, context.st, barY, _contentWidth, _isChatWide, !barInPlace);
|
||||
} else {
|
||||
HistoryView::MonoforumSenderBar::PaintFor(
|
||||
_forumThreadBarWidth = HistoryView::ForumThreadBar::PaintForGetWidth(
|
||||
p,
|
||||
context.st,
|
||||
view,
|
||||
_monoforumSenderUserpicView,
|
||||
senderY,
|
||||
_forumThreadBarUserpicView,
|
||||
barY,
|
||||
_contentWidth,
|
||||
_isChatWide);
|
||||
}
|
||||
@@ -2563,6 +2565,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||
Api::SendExistingPhoto(msg(), photo);
|
||||
}, &st::menuIconImportTheme);
|
||||
};
|
||||
auto rateTranscriptionItem = (HistoryItem*)(nullptr);
|
||||
const auto addDocumentActions = [&](not_null<DocumentData*> document, HistoryItem *item) {
|
||||
if (document->loading()) {
|
||||
_menu->addAction(tr::lng_context_cancel_download(tr::now), [=] {
|
||||
@@ -2622,6 +2625,11 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||
document);
|
||||
}, &st::menuIconStickers);
|
||||
}
|
||||
if (item
|
||||
&& (lnkIsVoice || document->isVideoMessage())
|
||||
&& Menu::HasRateTranscribeItem(item)) {
|
||||
rateTranscriptionItem = item;
|
||||
}
|
||||
|
||||
_menu->addAction(tr::lng_context_to_save_messages(tr::now), [=] {
|
||||
Api::SendExistingDocument(msg(), document);
|
||||
@@ -3048,6 +3056,12 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||
&st::menuIconGiftPremium);
|
||||
}
|
||||
}
|
||||
} else if (!rateTranscriptionItem && media->document()) {
|
||||
if ((media->document()->isVoiceMessage()
|
||||
|| media->document()->isVideoMessage())
|
||||
&& Menu::HasRateTranscribeItem(item)) {
|
||||
rateTranscriptionItem = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!item->isService() && view && actionText.isEmpty()) {
|
||||
@@ -3224,6 +3238,13 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||
_controller);
|
||||
}
|
||||
|
||||
if (!_menu->empty() && rateTranscriptionItem) {
|
||||
_menu->insertAction(0, base::make_unique_q<Menu::RateTranscribe>(
|
||||
_menu,
|
||||
_menu->st().menu,
|
||||
Menu::RateTranscribeCallbackFactory(rateTranscriptionItem)));
|
||||
}
|
||||
|
||||
if (_menu->empty()) {
|
||||
_menu = nullptr;
|
||||
return;
|
||||
@@ -3772,7 +3793,7 @@ void HistoryInner::toggleScrollDateShown() {
|
||||
void HistoryInner::repaintScrollDateCallback() {
|
||||
int updateTop = _visibleAreaTop;
|
||||
int updateHeight = st::msgServiceMargin.top() + st::msgServicePadding.top() + st::msgServiceFont->height + st::msgServicePadding.bottom();
|
||||
if (_history->amMonoforumAdmin()) {
|
||||
if (_history->hasForumThreadBars()) {
|
||||
updateHeight *= 2;
|
||||
}
|
||||
update(0, updateTop, width(), updateHeight);
|
||||
@@ -3827,7 +3848,7 @@ void HistoryInner::setShownPinned(HistoryItem *item) {
|
||||
void HistoryInner::enterEventHook(QEnterEvent *e) {
|
||||
_mouseActive = true;
|
||||
mouseActionUpdate(QCursor::pos());
|
||||
return TWidget::enterEventHook(e);
|
||||
return RpWidget::enterEventHook(e);
|
||||
}
|
||||
|
||||
void HistoryInner::leaveEventHook(QEvent *e) {
|
||||
@@ -3843,7 +3864,7 @@ void HistoryInner::leaveEventHook(QEvent *e) {
|
||||
setCursor(_cursor);
|
||||
}
|
||||
_mouseActive = false;
|
||||
return TWidget::leaveEventHook(e);
|
||||
return RpWidget::leaveEventHook(e);
|
||||
}
|
||||
|
||||
HistoryInner::~HistoryInner() {
|
||||
@@ -3865,7 +3886,7 @@ HistoryInner::~HistoryInner() {
|
||||
|
||||
bool HistoryInner::focusNextPrevChild(bool next) {
|
||||
if (_selected.empty()) {
|
||||
return TWidget::focusNextPrevChild(next);
|
||||
return RpWidget::focusNextPrevChild(next);
|
||||
} else {
|
||||
clearSelected();
|
||||
return true;
|
||||
@@ -4335,6 +4356,54 @@ void HistoryInner::mouseActionUpdate() {
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (!dragState.link) {
|
||||
enumerateForumThreadBars([&](not_null<Element*> view, int itemtop, int barTop) {
|
||||
// stop the enumeration if the bar is above our point
|
||||
if (barTop + dateHeight <= point.y()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto displayBar = view->displayForumThreadBar();
|
||||
auto barInPlace = displayBar;
|
||||
if (barInPlace) {
|
||||
const auto correctBarTop = itemtop + view->displayedDateHeight() + st::msgServiceMargin.top();
|
||||
barInPlace = (barTop < correctBarTop + st::msgServiceMargin.top());
|
||||
}
|
||||
|
||||
// stop enumeration if we've found a bar under the cursor
|
||||
if (barTop <= point.y()) {
|
||||
const auto item = view->data();
|
||||
auto barWidth = 0;
|
||||
if (const auto bar = view->Get<HistoryView::ForumThreadBar>()) {
|
||||
barWidth = bar->width;
|
||||
} else {
|
||||
barWidth = _forumThreadBarWidth;
|
||||
}
|
||||
auto barLeft = st::msgServiceMargin.left();
|
||||
auto maxwidth = _contentWidth;
|
||||
if (_isChatWide) {
|
||||
maxwidth = qMin(maxwidth, int32(st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left()));
|
||||
}
|
||||
auto widthForBar = maxwidth - st::msgServiceMargin.left() - st::msgServiceMargin.left();
|
||||
|
||||
barLeft += (widthForBar - barWidth) / 2;
|
||||
|
||||
if (point.x() >= barLeft && point.x() < barLeft + barWidth) {
|
||||
if (!_forumThreadBarLink) {
|
||||
_forumThreadBarLink = std::make_shared<Window::ForumThreadClickHandler>(item);
|
||||
} else {
|
||||
static_cast<Window::ForumThreadClickHandler*>(_forumThreadBarLink.get())->update(item);
|
||||
}
|
||||
dragState = TextState(
|
||||
nullptr,
|
||||
_forumThreadBarLink);
|
||||
_dragStateItem = session().data().message(dragState.itemId);
|
||||
lnkhost = view;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (!dragState.link) {
|
||||
StateRequest request;
|
||||
if (_mouseAction == MouseAction::Selecting) {
|
||||
|
||||
Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais
Referência em uma Nova Issue
Bloquear um usuário