Compare commits
53 Commits
window_bla
...
v6.0.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e15722eab | ||
|
|
719c209c7b | ||
|
|
d05ad44b84 | ||
|
|
b352c97479 | ||
|
|
063085a6bb | ||
|
|
b4bca16109 | ||
|
|
03770c52fe | ||
|
|
10fe5cdd5d | ||
|
|
57d459b917 | ||
|
|
fe26594f12 | ||
|
|
0d8065fc1f | ||
|
|
a3cdae1e94 | ||
|
|
29d77b649b | ||
|
|
9e190cee81 | ||
|
|
687bfd0f17 | ||
|
|
7d7df4f749 | ||
|
|
958dede319 | ||
|
|
76e814944d | ||
|
|
fbc1d75e9a | ||
|
|
b3c7ce05dc | ||
|
|
feea881e09 | ||
|
|
b978bc4876 | ||
|
|
f84181e7a5 | ||
|
|
313ae0f86c | ||
|
|
a39c018359 | ||
|
|
a09f57d908 | ||
|
|
596828cf78 | ||
|
|
44843aa9cd | ||
|
|
5f930cc4d1 | ||
|
|
7defad5d95 | ||
|
|
707af8a295 | ||
|
|
e0fb9ffbb0 | ||
|
|
d614de6f5e | ||
|
|
86b94b4723 | ||
|
|
5070300050 | ||
|
|
1919546441 | ||
|
|
1c3cd8d44b | ||
|
|
292296266f | ||
|
|
a3e8848bc8 | ||
|
|
ced146fc63 | ||
|
|
6ed79f6a0d | ||
|
|
15e4d86e92 | ||
|
|
5a29a7d2a3 | ||
|
|
034740ce46 | ||
|
|
a2847246e6 | ||
|
|
04479ad660 | ||
|
|
35d2fff593 | ||
|
|
2b93fe9a30 | ||
|
|
5c33a2bd5c | ||
|
|
a28f113105 | ||
|
|
eb7976a2ef | ||
|
|
15db1c0c30 | ||
|
|
2b83c95869 |
@@ -523,6 +523,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
|
||||
@@ -876,6 +878,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
|
||||
@@ -1119,6 +1123,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
|
||||
@@ -1274,6 +1280,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
|
||||
@@ -1307,6 +1315,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
|
||||
@@ -1427,10 +1437,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
|
||||
@@ -1625,8 +1631,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
|
||||
@@ -1659,6 +1663,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
|
||||
|
||||
@@ -377,6 +377,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";
|
||||
@@ -512,6 +515,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";
|
||||
@@ -552,12 +556,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";
|
||||
@@ -687,6 +694,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";
|
||||
@@ -1238,6 +1247,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";
|
||||
|
||||
@@ -2055,6 +2076,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.";
|
||||
@@ -4291,6 +4313,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";
|
||||
@@ -4302,6 +4325,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";
|
||||
@@ -6334,6 +6360,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", "Telegram FZ-LLC"
|
||||
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", "Telegram FZ-LLC"
|
||||
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"
|
||||
|
||||
@@ -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>(
|
||||
@@ -219,7 +218,6 @@ void ConfirmSubscriptionBox(
|
||||
box,
|
||||
tr::lng_channel_invite_subscription_title(),
|
||||
st::inviteLinkSubscribeBoxTitle),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
@@ -234,7 +232,6 @@ void ConfirmSubscriptionBox(
|
||||
Ui::Text::Bold),
|
||||
Ui::Text::WithEntities),
|
||||
st::inviteLinkSubscribeBoxAbout),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
Ui::AddSkip(content);
|
||||
box->addRow(
|
||||
@@ -250,7 +247,6 @@ void ConfirmSubscriptionBox(
|
||||
}),
|
||||
Ui::Text::RichLangValue),
|
||||
st::inviteLinkSubscribeBoxTerms),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
@@ -158,3 +166,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();
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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,7 +76,7 @@ void GiftCreditsBox(
|
||||
u"internal:stars_examples"_q);
|
||||
});
|
||||
content->add(
|
||||
Ui::CreateLabelWithCustomEmoji(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
tr::lng_credits_box_history_entry_gift_out_about(
|
||||
lt_user,
|
||||
@@ -86,7 +84,6 @@ void GiftCreditsBox(
|
||||
lt_link,
|
||||
std::move(link),
|
||||
Ui::Text::RichLangValue),
|
||||
Core::TextContext({ .session = &peer->session() }),
|
||||
st::creditsBoxAbout),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
|
||||
@@ -990,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);
|
||||
|
||||
@@ -513,7 +513,7 @@ void InviteForbiddenController::setComplexCover() {
|
||||
container->add(
|
||||
MakeShowOrLabel(container, tr::lng_invite_upgrade_or()),
|
||||
st::inviteForbiddenOrLabelPadding,
|
||||
style::al_top);
|
||||
style::al_justify);
|
||||
}
|
||||
container->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
|
||||
@@ -77,7 +77,9 @@ DefaultIconEmoji::DefaultIconEmoji(
|
||||
std::move(value) | rpl::start_with_next([=](DefaultIcon value) {
|
||||
_icon = value;
|
||||
_image = QImage();
|
||||
repaint();
|
||||
if (repaint) {
|
||||
repaint();
|
||||
}
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
|
||||
@@ -1200,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;
|
||||
|
||||
@@ -969,9 +969,9 @@ 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);
|
||||
@@ -981,7 +981,6 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
|
||||
box,
|
||||
tr::lng_credits_box_subscription_title(),
|
||||
st::creditsBoxAboutTitle),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
|
||||
Ui::AddSkip(content);
|
||||
@@ -990,7 +989,6 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
st::creditsTopupPrice),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
subtitle1->setMarkedText(
|
||||
tr::lng_credits_subscription_subtitle(
|
||||
@@ -1002,11 +1000,10 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
|
||||
Ui::Text::WithEntities),
|
||||
_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) {
|
||||
@@ -1038,7 +1035,6 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
|
||||
tr::lng_credits_box_out_about_link(tr::now)),
|
||||
Ui::Text::WithEntities),
|
||||
st::creditsBoxAboutDivider),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
|
||||
const auto button = box->addButton(tr::lng_box_ok(), [=] {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
@@ -371,16 +371,14 @@ void SendCreditsBox(
|
||||
? rpl::single(form->title)
|
||||
: tr::lng_credits_box_out_title(),
|
||||
st::settingsPremiumUserTitle),
|
||||
st::boxRowPadding,
|
||||
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);
|
||||
@@ -389,7 +387,6 @@ void SendCreditsBox(
|
||||
box,
|
||||
SendCreditsConfirmText(session, form.get()),
|
||||
st::creditsBoxAbout),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
|
||||
@@ -3151,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);
|
||||
|
||||
@@ -4919,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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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()));
|
||||
};
|
||||
@@ -323,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,
|
||||
|
||||
@@ -1042,13 +1042,12 @@ 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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -656,8 +656,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);
|
||||
|
||||
auto result = QByteArray();
|
||||
result.reserve(size);
|
||||
@@ -402,7 +403,8 @@ QByteArray Settings::serialize() const {
|
||||
<< SerializeVideoQuality(_videoQuality)
|
||||
<< qint32(_ivZoom.current())
|
||||
<< qint32(_systemDarkModeEnabled.current() ? 1 : 0)
|
||||
<< qint32(_quickDialogAction);
|
||||
<< qint32(_quickDialogAction)
|
||||
<< _notificationsVolume;
|
||||
}
|
||||
|
||||
Ensures(result.size() == size);
|
||||
@@ -532,6 +534,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()) {
|
||||
@@ -863,6 +866,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()"));
|
||||
@@ -1085,6 +1091,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 {
|
||||
@@ -1477,6 +1484,7 @@ void Settings::resetOnLastLogout() {
|
||||
_videoQuality = {};
|
||||
_chatFiltersHorizontal = false;
|
||||
_quickDialogAction = Dialogs::Ui::QuickDialogAction::Disabled;
|
||||
_notificationsVolume = 100;
|
||||
|
||||
_recentEmojiPreload.clear();
|
||||
_recentEmoji.clear();
|
||||
|
||||
@@ -950,6 +950,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:
|
||||
@@ -1093,6 +1100,8 @@ private:
|
||||
Dialogs::Ui::QuickDialogAction _quickDialogAction
|
||||
= Dialogs::Ui::QuickDialogAction::Disabled;
|
||||
|
||||
ushort _notificationsVolume = 100;
|
||||
|
||||
QByteArray _photoEditorBrush;
|
||||
|
||||
};
|
||||
|
||||
@@ -657,11 +657,7 @@ bool ResolveUsernameOrPhone(
|
||||
startToken = params.value(u"startapp"_q);
|
||||
}
|
||||
}
|
||||
const auto historyInNewWindow =
|
||||
(params.value(u"tdesktop_target"_q) == u"blank"_q);
|
||||
if (!historyInNewWindow) {
|
||||
controller->window().activate();
|
||||
}
|
||||
controller->window().activate();
|
||||
controller->showPeerByLink(Window::PeerByLinkInfo{
|
||||
.usernameOrId = domain,
|
||||
.phone = phone,
|
||||
@@ -712,7 +708,8 @@ bool ResolveUsernameOrPhone(
|
||||
: std::nullopt),
|
||||
.clickFromMessageId = myContext.itemId,
|
||||
.clickFromBotWebviewContext = myContext.botWebviewContext,
|
||||
.historyInNewWindow = historyInNewWindow,
|
||||
.historyInNewWindow =
|
||||
(params.value(u"tdesktop_target"_q) == u"blank"_q),
|
||||
});
|
||||
return true;
|
||||
}
|
||||
@@ -1920,8 +1917,6 @@ bool InternalPassportLink(const QString &url) {
|
||||
bool StartUrlRequiresActivate(const QString &url) {
|
||||
return Core::App().passcodeLocked()
|
||||
? true
|
||||
: url.contains(u"tdesktop_target"_q)
|
||||
? false
|
||||
: !InternalPassportLink(url);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
|
||||
constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
|
||||
constexpr auto AppName = "Telegram Desktop"_cs;
|
||||
constexpr auto AppFile = "Telegram"_cs;
|
||||
constexpr auto AppVersion = 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()) {
|
||||
|
||||
@@ -7,6 +7,7 @@ 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"
|
||||
@@ -194,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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
117
Telegram/SourceFiles/data/notify/data_peer_notify_volume.cpp
Normal file
117
Telegram/SourceFiles/data/notify/data_peer_notify_volume.cpp
Normal file
@@ -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
|
||||
45
Telegram/SourceFiles/data/notify/data_peer_notify_volume.h
Normal file
45
Telegram/SourceFiles/data/notify/data_peer_notify_volume.h
Normal file
@@ -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
|
||||
@@ -838,6 +838,8 @@ dialogsTopBarSuggestionAboutStyle: TextStyle(defaultTextStyle) {
|
||||
font: font(11px);
|
||||
}
|
||||
|
||||
dialogsMiniQuoteIcon: icon{{ "chat/mini_quote", dialogsTextFg }};
|
||||
|
||||
postsSearchIntroTitle: FlatLabel(defaultFlatLabel) {
|
||||
textFg: windowBoldFg;
|
||||
minWidth: 64px;
|
||||
|
||||
@@ -1240,6 +1240,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);
|
||||
@@ -1257,6 +1258,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||
.currentBg = currentBg(),
|
||||
.filter = _filterId,
|
||||
.now = ms,
|
||||
.searchLowerText = QStringView(searchLowerText),
|
||||
.width = fullWidth,
|
||||
.active = active,
|
||||
.selected = selected,
|
||||
@@ -1284,6 +1286,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);
|
||||
@@ -1331,6 +1334,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||
.currentBg = currentBg(),
|
||||
.filter = _filterId,
|
||||
.now = ms,
|
||||
.searchLowerText = QStringView(searchLowerText),
|
||||
.width = fullWidth,
|
||||
.active = active,
|
||||
.selected = selected,
|
||||
@@ -4147,14 +4151,13 @@ void InnerWidget::refreshEmpty() {
|
||||
.name = u"no_chats"_q,
|
||||
.sizeOverride = Size(st::changePhoneIconSize),
|
||||
});
|
||||
_emptyList->add(std::move(icon.widget), {}, style::al_top);
|
||||
_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),
|
||||
{},
|
||||
style::al_top);
|
||||
if (_state == WidgetState::Default) {
|
||||
icon.animate(anim::repeat::once);
|
||||
|
||||
@@ -1137,6 +1137,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)) {
|
||||
|
||||
@@ -1886,6 +1886,7 @@ void Suggestions::setupPostsSearch() {
|
||||
nullptr,
|
||||
{ .posts = true, .start = true },
|
||||
state.totalCount);
|
||||
_postsScroll->scrollToY(0);
|
||||
updatePostsSearchVisibleRange();
|
||||
}
|
||||
}, _postsWrap->lifetime());
|
||||
|
||||
@@ -186,7 +186,6 @@ void PostsSearchIntro::setup() {
|
||||
_content.get(),
|
||||
rpl::single(QString()),
|
||||
st::postsSearchIntroButton),
|
||||
{},
|
||||
style::al_top);
|
||||
_button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
|
||||
_footer = _content->add(
|
||||
|
||||
@@ -3464,6 +3464,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;
|
||||
|
||||
@@ -73,6 +73,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"
|
||||
@@ -926,8 +928,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;
|
||||
}
|
||||
|
||||
@@ -944,28 +946,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;
|
||||
}
|
||||
}
|
||||
@@ -978,7 +980,7 @@ void HistoryInner::enumerateMonoforumSenders(Method method) {
|
||||
return true;
|
||||
};
|
||||
|
||||
enumerateItems<EnumItemsDirection::BottomToTop>(senderCallback);
|
||||
enumerateItems<EnumItemsDirection::BottomToTop>(barCallback);
|
||||
}
|
||||
|
||||
TextSelection HistoryInner::computeRenderSelection(
|
||||
@@ -1393,31 +1395,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);
|
||||
}
|
||||
@@ -2500,6 +2502,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||
}, &st::menuIconStickers);
|
||||
}
|
||||
};
|
||||
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), [=] {
|
||||
@@ -2559,6 +2562,11 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||
document);
|
||||
}, &st::menuIconStickers);
|
||||
}
|
||||
if (item
|
||||
&& (lnkIsVoice || document->isVideoMessage())
|
||||
&& Menu::HasRateTranscribeItem(item)) {
|
||||
rateTranscriptionItem = item;
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef _DEBUG // Sometimes we need to save emoji to files.
|
||||
@@ -2972,6 +2980,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()) {
|
||||
@@ -3139,6 +3153,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;
|
||||
@@ -3677,7 +3698,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);
|
||||
@@ -4240,6 +4261,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) {
|
||||
|
||||
@@ -315,13 +315,13 @@ private:
|
||||
template <typename Method>
|
||||
void enumerateDates(Method method);
|
||||
|
||||
// This function finds all monoforum sender elements that are displayed and calls template method
|
||||
// This function finds all forum thread bar elements that are displayed and calls template method
|
||||
// for each found date element (from the bottom to the top) using enumerateItems() method.
|
||||
//
|
||||
// Method has "bool (*Method)(not_null<Element*> view, int itemtop, int dateTop)" signature
|
||||
// if it returns false the enumeration stops immediately.
|
||||
template <typename Method>
|
||||
void enumerateMonoforumSenders(Method method);
|
||||
void enumerateForumThreadBars(Method method);
|
||||
|
||||
void scrollDateCheck();
|
||||
void scrollDateHideByTimer();
|
||||
@@ -470,7 +470,8 @@ private:
|
||||
int _contentWidth = 0;
|
||||
int _historyPaddingTop = 0;
|
||||
int _revealHeight = 0;
|
||||
Ui::PeerUserpicView _monoforumSenderUserpicView;
|
||||
int _forumThreadBarWidth = 0;
|
||||
Ui::PeerUserpicView _forumThreadBarUserpicView;
|
||||
|
||||
// Save visible area coords for painting / pressing userpics.
|
||||
int _visibleAreaTop = 0;
|
||||
@@ -572,6 +573,7 @@ private:
|
||||
Element *_scrollDateLastItem = nullptr;
|
||||
int _scrollDateLastItemTop = 0;
|
||||
ClickHandlerPtr _scrollDateLink;
|
||||
ClickHandlerPtr _forumThreadBarLink;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/view/history_view_pinned_section.h"
|
||||
#include "history/view/history_view_pinned_bar.h"
|
||||
#include "history/view/history_view_group_call_bar.h"
|
||||
#include "history/view/history_view_group_members_widget.h"
|
||||
#include "history/view/history_view_item_preview.h"
|
||||
#include "history/view/history_view_reply.h"
|
||||
#include "history/view/history_view_requests_bar.h"
|
||||
@@ -122,7 +123,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/view/history_view_subsection_tabs.h"
|
||||
#include "history/view/history_view_translate_bar.h"
|
||||
#include "history/view/media/history_view_media.h"
|
||||
#include "profile/profile_block_group_members.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "chat_helpers/field_autocomplete.h"
|
||||
#include "chat_helpers/tabbed_panel.h"
|
||||
@@ -2105,6 +2105,18 @@ void HistoryWidget::setupShortcuts() {
|
||||
std::make_shared<Scheduled>(_history));
|
||||
return true;
|
||||
});
|
||||
if (showRecordButton()) {
|
||||
const auto isVoice = request->check(Command::RecordVoice, 1);
|
||||
const auto isRound = !isVoice
|
||||
&& request->check(Command::RecordRound, 1);
|
||||
(isVoice || isRound) && request->handle([=] {
|
||||
if (_voiceRecordBar) {
|
||||
_voiceRecordBar->startRecordingAndLock(isRound);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
if (session().supportMode()) {
|
||||
request->check(
|
||||
Command::SupportToggleMuted
|
||||
@@ -5806,11 +5818,7 @@ void HistoryWidget::showMembersDropdown() {
|
||||
if (!_membersDropdown) {
|
||||
_membersDropdown.create(this, st::membersInnerDropdown);
|
||||
_membersDropdown->setOwnedWidget(
|
||||
object_ptr<Profile::GroupMembersWidget>(
|
||||
this,
|
||||
controller(),
|
||||
_peer,
|
||||
st::membersInnerItem));
|
||||
object_ptr<HistoryView::GroupMembersWidget>(this, controller(), _peer));
|
||||
_membersDropdown->resizeToWidth(st::membersInnerWidth);
|
||||
|
||||
_membersDropdown->setMaxHeight(countMembersDropdownHeightMax());
|
||||
|
||||
@@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "chat_helpers/field_autocomplete.h"
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "core/shortcuts.h"
|
||||
#include "core/ui_integration.h"
|
||||
#include "data/notify/data_notify_settings.h"
|
||||
#include "data/data_changes.h"
|
||||
@@ -1180,6 +1181,17 @@ auto ComposeControls::sendContentRequests(SendRequestType requestType) const {
|
||||
_sendCustomRequests.events());
|
||||
}
|
||||
|
||||
rpl::producer<> ComposeControls::scrollToMaxRequests() const {
|
||||
return _field->submits() | rpl::filter([=]{
|
||||
if (_mode == Mode::Normal
|
||||
&& !_voiceRecordBar->isListenState()
|
||||
&& getTextWithAppliedMarkdown().text.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}) | rpl::to_empty;
|
||||
}
|
||||
|
||||
rpl::producer<Api::SendOptions> ComposeControls::sendRequests() const {
|
||||
return sendContentRequests(SendRequestType::Text);
|
||||
}
|
||||
@@ -2593,6 +2605,23 @@ void ComposeControls::initVoiceRecordBar() {
|
||||
) | rpl::start_with_next([=] {
|
||||
updateSendButtonType();
|
||||
}, _wrap->lifetime());
|
||||
|
||||
Shortcuts::Requests(
|
||||
) | rpl::filter([=] {
|
||||
return Ui::AppInFocus();
|
||||
}) | rpl::start_with_next([=](not_null<Shortcuts::Request*> request) {
|
||||
using Command = Shortcuts::Command;
|
||||
const auto isVoice = request->check(Command::RecordVoice, 1);
|
||||
const auto isRound = !isVoice
|
||||
&& request->check(Command::RecordRound, 1);
|
||||
(isVoice || isRound) && request->handle([=] {
|
||||
if (_voiceRecordBar) {
|
||||
_voiceRecordBar->startRecordingAndLock(isRound);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}, _voiceRecordBar->lifetime());
|
||||
}
|
||||
|
||||
void ComposeControls::updateWrappingVisibility() {
|
||||
|
||||
@@ -183,6 +183,7 @@ public:
|
||||
-> rpl::producer<ReplyNextRequest>;
|
||||
[[nodiscard]] rpl::producer<> focusRequests() const;
|
||||
[[nodiscard]] rpl::producer<> showScheduledRequests() const;
|
||||
[[nodiscard]] rpl::producer<> scrollToMaxRequests() const;
|
||||
|
||||
using MimeDataHook = Fn<bool(
|
||||
not_null<const QMimeData*> data,
|
||||
|
||||
@@ -54,7 +54,9 @@ using SearchRequest = Api::MessagesSearchMerged::Request;
|
||||
|
||||
class Row final : public PeerListRow {
|
||||
public:
|
||||
explicit Row(std::unique_ptr<Dialogs::FakeRow> fakeRow);
|
||||
explicit Row(
|
||||
std::unique_ptr<Dialogs::FakeRow> fakeRow,
|
||||
not_null<QString*> query);
|
||||
|
||||
[[nodiscard]] FullMsgId fullId() const;
|
||||
|
||||
@@ -73,15 +75,17 @@ public:
|
||||
private:
|
||||
const std::unique_ptr<Dialogs::FakeRow> _fakeRow;
|
||||
|
||||
not_null<QString*> _query;
|
||||
int _outerWidth = 0;
|
||||
|
||||
};
|
||||
|
||||
Row::Row(std::unique_ptr<Dialogs::FakeRow> fakeRow)
|
||||
Row::Row(std::unique_ptr<Dialogs::FakeRow> fakeRow, not_null<QString*> query)
|
||||
: PeerListRow(
|
||||
fakeRow->searchInChat().history()->peer,
|
||||
fakeRow->item()->fullId().msg.bare)
|
||||
, _fakeRow(std::move(fakeRow)) {
|
||||
, _fakeRow(std::move(fakeRow))
|
||||
, _query(query) {
|
||||
}
|
||||
|
||||
FullMsgId Row::fullId() const {
|
||||
@@ -116,9 +120,11 @@ void Row::elementsPaint(
|
||||
.st = &st::defaultDialogRow,
|
||||
.currentBg = st::dialogsBg,
|
||||
.now = crl::now(),
|
||||
.searchLowerText = QStringView(*_query),
|
||||
.width = outerWidth,
|
||||
.selected = selected,
|
||||
.paused = p.inactive(),
|
||||
.search = true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -134,6 +140,7 @@ public:
|
||||
void loadMoreRows() override;
|
||||
|
||||
void addItems(const MessageIdsList &ids, bool clear);
|
||||
void setQuery(const QString &query);
|
||||
|
||||
[[nodiscard]] rpl::producer<FullMsgId> showItemRequests() const;
|
||||
[[nodiscard]] rpl::producer<> searchMoreRequests() const;
|
||||
@@ -145,6 +152,8 @@ private:
|
||||
rpl::event_stream<> _searchMoreRequests;
|
||||
rpl::event_stream<> _resetScrollRequests;
|
||||
|
||||
QString _query;
|
||||
|
||||
};
|
||||
|
||||
ListController::ListController(not_null<History*> history)
|
||||
@@ -201,7 +210,8 @@ void ListController::addItems(const MessageIdsList &ids, bool clear) {
|
||||
std::make_unique<Dialogs::FakeRow>(
|
||||
key,
|
||||
item,
|
||||
[=] { delegate()->peerListUpdateRow(*shared); }));
|
||||
[=] { delegate()->peerListUpdateRow(*shared); }),
|
||||
&_query);
|
||||
*shared = row.get();
|
||||
delegate()->peerListAppendRow(std::move(row));
|
||||
}
|
||||
@@ -214,6 +224,10 @@ void ListController::addItems(const MessageIdsList &ids, bool clear) {
|
||||
}
|
||||
}
|
||||
|
||||
void ListController::setQuery(const QString &query) {
|
||||
_query = query;
|
||||
}
|
||||
|
||||
struct List {
|
||||
base::unique_qptr<Ui::RpWidget> container;
|
||||
std::unique_ptr<ListController> controller;
|
||||
@@ -913,6 +927,9 @@ ComposeSearch::Inner::Inner(
|
||||
search.topMsgId = _topMsgId;
|
||||
_apiSearch.clear();
|
||||
_apiSearch.search(search);
|
||||
|
||||
_list.controller->addItems({}, true);
|
||||
_list.controller->setQuery(_apiSearch.request().query);
|
||||
}, _topBar->lifetime());
|
||||
|
||||
_topBar->queryChanges(
|
||||
|
||||
@@ -1075,7 +1075,7 @@ void DraftOptionsBox(
|
||||
|
||||
state->wrap = box->addRow(
|
||||
object_ptr<PreviewWrap>(box, args.history),
|
||||
{});
|
||||
style::margins());
|
||||
state->wrap->draggingScrollDelta(
|
||||
) | rpl::start_with_next([=](int delta) {
|
||||
box->scrollByDraggingDelta(delta);
|
||||
|
||||
@@ -394,7 +394,6 @@ void ChooseSuggestPriceBox(
|
||||
box,
|
||||
std::move(title),
|
||||
st::settingsPremiumUserTitle),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/dynamic_image.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/widgets/fields/input_field.h" // ShouldSubmit.
|
||||
#include "ui/widgets/tooltip.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/ui_utility.h"
|
||||
@@ -130,10 +131,6 @@ void SoundedPreview::subscribeToUpdates(Fn<void()> callback) {
|
||||
_repaint = std::move(callback);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto InactiveColor(const QColor &c) {
|
||||
return QColor(c.red(), c.green(), c.blue(), kInactiveWaveformBarAlpha);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto Progress(int low, int high) {
|
||||
return std::clamp(float64(low) / high, 0., 1.);
|
||||
}
|
||||
@@ -442,6 +439,7 @@ TTLButton::TTLButton(
|
||||
r.top()
|
||||
+ r.height()
|
||||
- size.height()
|
||||
- st::historyRecordTooltipSkip
|
||||
+ st::historyRecordTooltip.padding.top());
|
||||
});
|
||||
}, _tooltip->lifetime());
|
||||
@@ -606,7 +604,8 @@ ListenWrap::ListenWrap(
|
||||
, _playPauseSt(st::mediaPlayerButton)
|
||||
, _playPauseButton(base::make_unique_q<Ui::AbstractButton>(parent))
|
||||
, _activeWaveformBar(st::historyRecordVoiceFgActiveIcon->c)
|
||||
, _inactiveWaveformBar(InactiveColor(_activeWaveformBar))
|
||||
, _inactiveWaveformBar(
|
||||
anim::with_alpha(_activeWaveformBar, kInactiveWaveformBarAlpha))
|
||||
, _playPause(_playPauseSt, [=] { _playPauseButton->update(); }) {
|
||||
init();
|
||||
}
|
||||
@@ -1655,6 +1654,7 @@ void VoiceRecordBar::init() {
|
||||
|
||||
auto callback = [=](float64 value) {
|
||||
_lock->requestPaintLockToStopProgress(value);
|
||||
_level->requestPaintColor(activeAnimationRatio());
|
||||
update();
|
||||
updateTTLGeometry(TTLAnimationType::RightLeft, value);
|
||||
};
|
||||
@@ -1678,11 +1678,7 @@ void VoiceRecordBar::init() {
|
||||
if (_startRecordingFilter && _startRecordingFilter()) {
|
||||
return;
|
||||
}
|
||||
_recordingTipRequire = crl::now();
|
||||
_recordingVideo = (_send->type() == Ui::SendButton::Type::Round);
|
||||
_fullRecord = false;
|
||||
_ttlButton = nullptr;
|
||||
_lock->setRecordingVideo(_recordingVideo);
|
||||
prepareOnSendPress();
|
||||
_startTimer.callOnce(st::universalDuration);
|
||||
} else if (e->type() == QEvent::MouseButtonRelease) {
|
||||
checkTipRequired();
|
||||
@@ -1712,6 +1708,14 @@ void VoiceRecordBar::init() {
|
||||
initLevelGeometry();
|
||||
}
|
||||
|
||||
void VoiceRecordBar::prepareOnSendPress() {
|
||||
_recordingTipRequire = crl::now();
|
||||
_recordingVideo = (_send->type() == Ui::SendButton::Type::Round);
|
||||
_fullRecord = false;
|
||||
_ttlButton = nullptr;
|
||||
_lock->setRecordingVideo(_recordingVideo);
|
||||
}
|
||||
|
||||
void VoiceRecordBar::activeAnimate(bool active) {
|
||||
const auto to = active ? 1. : 0.;
|
||||
if (_activeAnimation.animating()) {
|
||||
@@ -1802,6 +1806,24 @@ void VoiceRecordBar::initLevelGeometry() {
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
void VoiceRecordBar::startRecordingAndLock(bool round) {
|
||||
{
|
||||
auto sendState = _send->state();
|
||||
sendState.type = round
|
||||
? Ui::SendButton::Type::Round
|
||||
: Ui::SendButton::Type::Record;
|
||||
_send->setState(std::move(sendState));
|
||||
}
|
||||
if (_startRecordingFilter && _startRecordingFilter()) {
|
||||
return;
|
||||
}
|
||||
prepareOnSendPress();
|
||||
|
||||
_lock->show();
|
||||
_lock->requestPaintProgress(1.);
|
||||
startRecording();
|
||||
}
|
||||
|
||||
void VoiceRecordBar::startRecording() {
|
||||
if (isRecording()) {
|
||||
return;
|
||||
@@ -1942,6 +1964,40 @@ void VoiceRecordBar::startRecording() {
|
||||
stop(_inField.current());
|
||||
}
|
||||
}, _recordingLifetime);
|
||||
|
||||
_listenChanges.events_starting_with(
|
||||
rpl::empty_value()
|
||||
) | rpl::filter([=] {
|
||||
return _listen == nullptr;
|
||||
}) | rpl::start_with_next([=] {
|
||||
auto keyFilterCallback = [=](not_null<QEvent*> e) {
|
||||
using Result = base::EventFilterResult;
|
||||
if (_send->type() != Ui::SendButton::Type::Record
|
||||
&& _send->type() != Ui::SendButton::Type::Round) {
|
||||
return Result::Continue;
|
||||
}
|
||||
switch(e->type()) {
|
||||
case QEvent::KeyPress: {
|
||||
if (!_warningShown
|
||||
&& isRecordingLocked()
|
||||
&& Ui::ShouldSubmit(
|
||||
static_cast<QKeyEvent*>(e.get()),
|
||||
Core::App().settings().sendSubmitWay())) {
|
||||
stop(true);
|
||||
return Result::Cancel;
|
||||
}
|
||||
return Result::Continue;
|
||||
}
|
||||
default: return Result::Continue;
|
||||
}
|
||||
};
|
||||
|
||||
_keyFilterInRecordingState = base::unique_qptr{
|
||||
base::install_event_filter(
|
||||
QCoreApplication::instance(),
|
||||
std::move(keyFilterCallback)).get()
|
||||
};
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
void VoiceRecordBar::checkTipRequired() {
|
||||
@@ -2015,6 +2071,7 @@ void VoiceRecordBar::hideFast() {
|
||||
_lock->hide();
|
||||
_level->hide();
|
||||
[[maybe_unused]] const auto s = takeTTLState();
|
||||
_keyFilterInRecordingState = nullptr;
|
||||
}
|
||||
|
||||
void VoiceRecordBar::stopRecording(StopType type, bool ttlBeforeHide) {
|
||||
@@ -2145,8 +2202,8 @@ void VoiceRecordBar::startRedCircleAnimation() {
|
||||
if (anim::Disabled()) {
|
||||
return;
|
||||
}
|
||||
const auto animation = _recordingLifetime
|
||||
.make_state<Ui::Animations::Basic>();
|
||||
const auto animation
|
||||
= _recordingLifetime.make_state<Ui::Animations::Basic>();
|
||||
animation->init([=](crl::time now) {
|
||||
const auto diffTime = now - animation->started();
|
||||
_redCircleProgress = std::abs(std::sin(diffTime / 400.));
|
||||
@@ -2298,6 +2355,9 @@ bool VoiceRecordBar::hasDuration() const {
|
||||
}
|
||||
|
||||
float64 VoiceRecordBar::activeAnimationRatio() const {
|
||||
if (isRecordingLocked()) {
|
||||
return 1.;
|
||||
}
|
||||
return _activeAnimation.value(_inField.current() ? 1. : 0.);
|
||||
}
|
||||
|
||||
@@ -2352,6 +2412,7 @@ void VoiceRecordBar::orderControls() {
|
||||
}
|
||||
|
||||
void VoiceRecordBar::installListenStateFilter() {
|
||||
_keyFilterInRecordingState = nullptr;
|
||||
auto keyFilterCallback = [=](not_null<QEvent*> e) {
|
||||
using Result = base::EventFilterResult;
|
||||
if (!(_send->type() == Ui::SendButton::Type::Send
|
||||
|
||||
@@ -78,7 +78,8 @@ public:
|
||||
Fn<void()> &&callback,
|
||||
anim::type animated = anim::type::instant);
|
||||
|
||||
void startRecording();
|
||||
void startRecordingAndLock(bool round);
|
||||
|
||||
void finishAnimating();
|
||||
void hideAnimated();
|
||||
void hideFast();
|
||||
@@ -145,6 +146,9 @@ private:
|
||||
void startRedCircleAnimation();
|
||||
void installListenStateFilter();
|
||||
|
||||
void startRecording();
|
||||
void prepareOnSendPress();
|
||||
|
||||
[[nodiscard]] bool isTypeRecord() const;
|
||||
[[nodiscard]] bool hasDuration() const;
|
||||
|
||||
@@ -194,6 +198,8 @@ private:
|
||||
FilterCallback _startRecordingFilter;
|
||||
FilterCallback _hasTTLFilter;
|
||||
|
||||
base::unique_qptr<QObject> _keyFilterInRecordingState;
|
||||
|
||||
bool _warningShown = false;
|
||||
bool _pauseInsteadSend = false;
|
||||
|
||||
|
||||
@@ -810,6 +810,11 @@ void ChatWidget::setupComposeControls() {
|
||||
send(options);
|
||||
}, lifetime());
|
||||
|
||||
_composeControls->scrollToMaxRequests(
|
||||
) | rpl::start_with_next([=] {
|
||||
listScrollTo(_scroll->scrollTopMax());
|
||||
}, lifetime());
|
||||
|
||||
_composeControls->sendVoiceRequests(
|
||||
) | rpl::start_with_next([=](const ComposeControls::VoiceToSend &data) {
|
||||
sendVoice(data);
|
||||
|
||||
@@ -44,6 +44,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/boxes/report_box_graphics.h"
|
||||
#include "ui/ui_utility.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_send.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/boxes/show_or_premium_box.h"
|
||||
@@ -338,6 +340,16 @@ void AddDocumentActions(
|
||||
const auto controller = list->controller();
|
||||
AddSaveSoundForNotifications(menu, item, document, controller);
|
||||
}
|
||||
if ((document->isVoiceMessage()
|
||||
|| document->isVideoMessage())
|
||||
&& Menu::HasRateTranscribeItem(item)) {
|
||||
if (!menu->empty()) {
|
||||
menu->insertAction(0, base::make_unique_q<Menu::RateTranscribe>(
|
||||
menu,
|
||||
menu->st().menu,
|
||||
Menu::RateTranscribeCallbackFactory(item)));
|
||||
}
|
||||
}
|
||||
AddSaveDocumentAction(menu, item, document, list);
|
||||
AddCopyFilename(
|
||||
menu,
|
||||
|
||||
@@ -101,59 +101,6 @@ Element *MousedElement/* = nullptr*/;
|
||||
return session->tryResolveWindow();
|
||||
}
|
||||
|
||||
[[nodiscard]] TextSelection FindSearchQueryHighlight(
|
||||
const QString &text,
|
||||
const QString &query) {
|
||||
const auto lower = query.toLower();
|
||||
const auto inside = text.toLower();
|
||||
const auto find = [&](QStringView part) {
|
||||
auto skip = 0;
|
||||
if (const auto from = inside.indexOf(part, skip); from >= 0) {
|
||||
if (!from || !inside[from - 1].isLetterOrNumber()) {
|
||||
return int(from);
|
||||
}
|
||||
skip = from + 1;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
if (const auto from = find(lower); from >= 0) {
|
||||
const auto till = from + query.size();
|
||||
if (till >= inside.size() || !inside[till].isLetterOrNumber()) {
|
||||
return { uint16(from), uint16(till) };
|
||||
}
|
||||
}
|
||||
const auto tillEndOfWord = [&](int from) {
|
||||
for (auto till = from + 1; till != inside.size(); ++till) {
|
||||
if (!inside[till].isLetterOrNumber()) {
|
||||
return TextSelection{ uint16(from), uint16(till) };
|
||||
}
|
||||
}
|
||||
return TextSelection{ uint16(from), uint16(inside.size()) };
|
||||
};
|
||||
const auto words = QStringView(lower).split(
|
||||
QRegularExpression(
|
||||
u"[\\W]"_q,
|
||||
QRegularExpression::UseUnicodePropertiesOption),
|
||||
Qt::SkipEmptyParts);
|
||||
for (const auto &word : words) {
|
||||
const auto length = int(word.size());
|
||||
const auto cut = length / 2;
|
||||
const auto part = word.mid(0, length - cut);
|
||||
const auto offset = find(part);
|
||||
if (offset < 0) {
|
||||
continue;
|
||||
}
|
||||
for (auto i = 0; i != cut; ++i) {
|
||||
const auto part = word.mid(0, length - i);
|
||||
if (const auto from = find(part); from >= 0) {
|
||||
return tillEndOfWord(from);
|
||||
}
|
||||
}
|
||||
return tillEndOfWord(offset);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] TextSelection ApplyModificationsFrom(
|
||||
TextSelection result,
|
||||
const Ui::Text::String &text) {
|
||||
@@ -480,20 +427,37 @@ void DateBadge::paint(
|
||||
ServiceMessagePainter::PaintDate(p, st, text, width, y, w, chatWide);
|
||||
}
|
||||
|
||||
void MonoforumSenderBar::init(
|
||||
void ForumThreadBar::init(
|
||||
not_null<PeerData*> parentChat,
|
||||
not_null<PeerData*> peer) {
|
||||
sender = peer;
|
||||
text.setText(st::semiboldTextStyle, peer->name());
|
||||
not_null<Data::Thread*> thread) {
|
||||
this->thread = thread;
|
||||
const auto sublist = thread->asSublist();
|
||||
if (sublist) {
|
||||
text.setText(st::semiboldTextStyle, sublist->sublistPeer()->name());
|
||||
} else if (const auto topic = thread->asTopic()) {
|
||||
text.setMarkedText(
|
||||
st::semiboldTextStyle,
|
||||
topic->titleWithIconOrLogo(),
|
||||
kMarkupTextOptions,
|
||||
Core::TextContext({ .session = &topic->session() }));
|
||||
}
|
||||
const auto skip = st::monoforumBarUserpicSkip;
|
||||
const auto userpic = st::msgServicePadding.top()
|
||||
+ st::msgServiceFont->height
|
||||
+ st::msgServicePadding.bottom()
|
||||
- 2 * skip;
|
||||
width = skip + userpic + skip * 2 + text.maxWidth() + st::msgServicePadding.right();
|
||||
const auto userpic = sublist
|
||||
? (st::msgServicePadding.top()
|
||||
+ st::msgServiceFont->height
|
||||
+ st::msgServicePadding.bottom()
|
||||
- 2 * skip)
|
||||
: (st::msgServicePadding.left() - 3 * skip);
|
||||
|
||||
width = skip
|
||||
+ userpic
|
||||
+ skip * 2
|
||||
+ text.maxWidth()
|
||||
+ st::topicButtonArrowSkip
|
||||
+ st::msgServicePadding.right();
|
||||
}
|
||||
|
||||
int MonoforumSenderBar::height() const {
|
||||
int ForumThreadBar::height() const {
|
||||
return st::msgServiceMargin.top()
|
||||
+ st::msgServicePadding.top()
|
||||
+ st::msgServiceFont->height
|
||||
@@ -501,17 +465,29 @@ int MonoforumSenderBar::height() const {
|
||||
+ st::msgServiceMargin.bottom();
|
||||
}
|
||||
|
||||
void MonoforumSenderBar::paint(
|
||||
void ForumThreadBar::paint(
|
||||
Painter &p,
|
||||
not_null<const Ui::ChatStyle*> st,
|
||||
int y,
|
||||
int w,
|
||||
bool chatWide,
|
||||
bool skipPatternLine) const {
|
||||
Paint(p, st, sender, text, width, view, y, w, chatWide, skipPatternLine);
|
||||
if (const auto strong = thread.get()) {
|
||||
Paint(
|
||||
p,
|
||||
st,
|
||||
strong,
|
||||
text,
|
||||
width,
|
||||
view,
|
||||
y,
|
||||
w,
|
||||
chatWide,
|
||||
skipPatternLine);
|
||||
}
|
||||
}
|
||||
|
||||
void MonoforumSenderBar::PaintFor(
|
||||
int ForumThreadBar::PaintForGetWidth(
|
||||
Painter &p,
|
||||
not_null<const Ui::ChatStyle*> st,
|
||||
not_null<Element*> itemView,
|
||||
@@ -519,31 +495,49 @@ void MonoforumSenderBar::PaintFor(
|
||||
int y,
|
||||
int w,
|
||||
bool chatWide) {
|
||||
const auto sublist = itemView->data()->savedSublist();
|
||||
const auto sender = (sublist && sublist->parentChat())
|
||||
? sublist->sublistPeer().get()
|
||||
const auto item = itemView->data();
|
||||
const auto topic = item->topic();
|
||||
const auto sublist = item->savedSublist();
|
||||
const auto sender = topic
|
||||
? (Data::Thread*)topic
|
||||
: (sublist && sublist->parentChat())
|
||||
? (Data::Thread*)sublist
|
||||
: nullptr;
|
||||
if (!sender || sender->isMonoforum()) {
|
||||
return;
|
||||
auto text = Ui::Text::String();
|
||||
if (!sender
|
||||
|| !topic
|
||||
|| (sublist && sublist->sublistPeer()->isMonoforum())) {
|
||||
return 0;
|
||||
} else if (topic) {
|
||||
text.setMarkedText(
|
||||
st::semiboldTextStyle,
|
||||
topic->titleWithIconOrLogo(),
|
||||
kMarkupTextOptions,
|
||||
Core::TextContext({ .session = &topic->session() }));
|
||||
} else {
|
||||
text.setText(st::semiboldTextStyle, sublist->sublistPeer()->name());
|
||||
}
|
||||
auto text = Ui::Text::String(st::semiboldTextStyle, sender->name());
|
||||
const auto skip = st::monoforumBarUserpicSkip;
|
||||
const auto userpic = st::msgServicePadding.top()
|
||||
+ st::msgServiceFont->height
|
||||
+ st::msgServicePadding.bottom()
|
||||
- 2 * skip;
|
||||
const auto userpic = sublist
|
||||
? (st::msgServicePadding.top()
|
||||
+ st::msgServiceFont->height
|
||||
+ st::msgServicePadding.bottom()
|
||||
- 2 * skip)
|
||||
: (st::msgServicePadding.left() - 3 * skip);
|
||||
const auto width = skip
|
||||
+ userpic
|
||||
+ skip * 2
|
||||
+ text.maxWidth()
|
||||
+ st::topicButtonArrowSkip
|
||||
+ st::msgServicePadding.right();
|
||||
Paint(p, st, sender, text, width, userpicView, y, w, chatWide, true);
|
||||
return width;
|
||||
}
|
||||
|
||||
void MonoforumSenderBar::Paint(
|
||||
void ForumThreadBar::Paint(
|
||||
Painter &p,
|
||||
not_null<const Ui::ChatStyle*> st,
|
||||
not_null<PeerData*> sender,
|
||||
not_null<Data::Thread*> thread,
|
||||
const Ui::Text::String &text,
|
||||
int width,
|
||||
Ui::PeerUserpicView &view,
|
||||
@@ -551,8 +545,6 @@ void MonoforumSenderBar::Paint(
|
||||
int w,
|
||||
bool chatWide,
|
||||
bool skipPatternLine) {
|
||||
Expects(sender != nullptr);
|
||||
|
||||
int left = st::msgServiceMargin.left();
|
||||
const auto maxwidth = chatWide
|
||||
? std::min(w, WideChatWidth())
|
||||
@@ -581,24 +573,47 @@ void MonoforumSenderBar::Paint(
|
||||
p.drawLine(left + use, top, 2 * w, top);
|
||||
}
|
||||
|
||||
const auto userpic = st::msgServicePadding.top()
|
||||
+ st::msgServiceFont->height
|
||||
+ st::msgServicePadding.bottom()
|
||||
- 2 * skip;
|
||||
const auto available = use - (skip + userpic + skip * 2 + st::msgServicePadding.right());
|
||||
const auto sublist = thread->asSublist();
|
||||
const auto userpic = sublist
|
||||
? (st::msgServicePadding.top()
|
||||
+ st::msgServiceFont->height
|
||||
+ st::msgServicePadding.bottom()
|
||||
- 2 * skip)
|
||||
: (st::msgServicePadding.left() - 3 * skip);
|
||||
const auto available = use
|
||||
- (skip
|
||||
+ userpic
|
||||
+ skip * 2
|
||||
+ st::topicButtonArrowSkip
|
||||
+ st::msgServicePadding.right());
|
||||
|
||||
sender->paintUserpic(p, view, left + skip, y + st::msgServiceMargin.top() + skip, userpic);
|
||||
if (sublist) {
|
||||
sublist->sublistPeer()->paintUserpic(
|
||||
p,
|
||||
view,
|
||||
left + skip,
|
||||
y + st::msgServiceMargin.top() + skip,
|
||||
userpic);
|
||||
}
|
||||
|
||||
const auto textLeft = left + skip + userpic + skip * 2;
|
||||
const auto textTop = y
|
||||
+ st::msgServiceMargin.top()
|
||||
+ st::msgServicePadding.top();
|
||||
p.setFont(st::msgServiceFont);
|
||||
p.setPen(st->msgServiceFg());
|
||||
text.draw(p, {
|
||||
.position = {
|
||||
left + skip + userpic + skip * 2,
|
||||
y + st::msgServiceMargin.top() + st::msgServicePadding.top(),
|
||||
},
|
||||
.position = { textLeft, textTop },
|
||||
.availableWidth = available,
|
||||
.elisionLines = 1,
|
||||
});
|
||||
|
||||
st::topicButtonArrow.paint(
|
||||
p,
|
||||
textLeft + available + st::topicButtonArrowPosition.x(),
|
||||
textTop + st::topicButtonArrowPosition.y(),
|
||||
w,
|
||||
st->msgServiceFg()->c);
|
||||
}
|
||||
|
||||
void ServicePreMessage::init(
|
||||
@@ -1420,7 +1435,7 @@ void Element::validateTextSkipBlock(bool has, int width, int height) {
|
||||
}
|
||||
|
||||
void Element::previousInBlocksChanged() {
|
||||
recountMonoforumSenderBarInBlocks();
|
||||
recountThreadBarInBlocks();
|
||||
recountDisplayDateInBlocks();
|
||||
recountAttachToPreviousInBlocks();
|
||||
}
|
||||
@@ -1457,7 +1472,7 @@ bool Element::computeIsAttachToPrevious(not_null<Element*> previous) {
|
||||
if (!Has<DateBadge>()
|
||||
&& !Has<UnreadBar>()
|
||||
&& !Has<ServicePreMessage>()
|
||||
&& !Has<MonoforumSenderBar>()) {
|
||||
&& !Has<ForumThreadBar>()) {
|
||||
const auto prev = previous->data();
|
||||
const auto previousMarkup = prev->inlineReplyMarkup();
|
||||
const auto possible = (std::abs(prev->date() - item->date())
|
||||
@@ -1569,12 +1584,12 @@ bool Element::isInOneDayWithPrevious() const {
|
||||
return !data()->isEmpty() && !displayDate();
|
||||
}
|
||||
|
||||
bool Element::displayMonoforumSender() const {
|
||||
return Has<MonoforumSenderBar>();
|
||||
bool Element::displayForumThreadBar() const {
|
||||
return Has<ForumThreadBar>();
|
||||
}
|
||||
|
||||
bool Element::isInOneBunchWithPrevious() const {
|
||||
return !data()->isEmpty() && !displayMonoforumSender();
|
||||
return !data()->isEmpty() && !displayForumThreadBar();
|
||||
}
|
||||
|
||||
void Element::recountAttachToPreviousInBlocks() {
|
||||
@@ -1595,34 +1610,49 @@ void Element::recountAttachToPreviousInBlocks() {
|
||||
setAttachToPrevious(attachToPrevious, previous);
|
||||
}
|
||||
|
||||
void Element::recountMonoforumSenderBarInBlocks() {
|
||||
void Element::recountThreadBarInBlocks() {
|
||||
const auto item = data();
|
||||
const auto topic = item->topic();
|
||||
const auto sublist = item->savedSublist();
|
||||
const auto parentChat = sublist ? sublist->parentChat() : nullptr;
|
||||
const auto barPeer = [&]() -> PeerData* {
|
||||
const auto parentChat = (topic && topic->channel()->useSubsectionTabs())
|
||||
? topic->channel().get()
|
||||
: sublist
|
||||
? sublist->parentChat()
|
||||
: nullptr;
|
||||
const auto barThread = [&]() -> Data::Thread* {
|
||||
if (!parentChat
|
||||
|| isHidden()
|
||||
|| item->isEmpty()
|
||||
|| item->isSponsored()) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto sublistPeer = sublist->sublistPeer();
|
||||
if (const auto previous = previousDisplayedInBlocks()) {
|
||||
const auto prev = previous->data();
|
||||
if (const auto prevSublist = prev->savedSublist()) {
|
||||
if (const auto prevTopic = prev->topic()) {
|
||||
Assert(prevTopic->channel() == parentChat);
|
||||
const auto topicRootId = topic->rootId();
|
||||
if (prevTopic->rootId() == topicRootId) {
|
||||
return nullptr;
|
||||
}
|
||||
} else if (const auto prevSublist = prev->savedSublist()) {
|
||||
Assert(prevSublist->parentChat() == parentChat);
|
||||
const auto sublistPeer = sublist->sublistPeer();
|
||||
if (prevSublist->sublistPeer() == sublistPeer) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (sublistPeer == parentChat) ? nullptr : sublistPeer.get();
|
||||
return topic
|
||||
? (Data::Thread*)topic
|
||||
: (sublist && sublist->sublistPeer() != parentChat)
|
||||
? (Data::Thread*)sublist
|
||||
: nullptr;
|
||||
}();
|
||||
if (barPeer && !Has<MonoforumSenderBar>()) {
|
||||
AddComponents(MonoforumSenderBar::Bit());
|
||||
Get<MonoforumSenderBar>()->init(parentChat, barPeer);
|
||||
} else if (!barPeer && Has<MonoforumSenderBar>()) {
|
||||
RemoveComponents(MonoforumSenderBar::Bit());
|
||||
if (barThread && !Has<ForumThreadBar>()) {
|
||||
AddComponents(ForumThreadBar::Bit());
|
||||
Get<ForumThreadBar>()->init(parentChat, barThread);
|
||||
} else if (!barThread && Has<ForumThreadBar>()) {
|
||||
RemoveComponents(ForumThreadBar::Bit());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2528,4 +2558,60 @@ Window::SessionController *ExtractController(const ClickContext &context) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TextSelection FindSearchQueryHighlight(
|
||||
const QString &text,
|
||||
const QString &query) {
|
||||
const auto lower = query.toLower();
|
||||
return FindSearchQueryHighlight(text, QStringView(lower));
|
||||
}
|
||||
|
||||
TextSelection FindSearchQueryHighlight(
|
||||
const QString &text,
|
||||
QStringView lower) {
|
||||
const auto inside = text.toLower();
|
||||
const auto find = [&](QStringView part) {
|
||||
auto skip = 0;
|
||||
if (const auto from = inside.indexOf(part, skip); from >= 0) {
|
||||
if (!from || !inside[from - 1].isLetterOrNumber()) {
|
||||
return int(from);
|
||||
}
|
||||
skip = from + 1;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
if (const auto from = find(lower); from >= 0) {
|
||||
const auto till = from + lower.size();
|
||||
if (till >= inside.size()
|
||||
|| !(inside.begin() + till)->isLetterOrNumber()) {
|
||||
return { uint16(from), uint16(till) };
|
||||
}
|
||||
}
|
||||
const auto tillEndOfWord = [&](int from) {
|
||||
for (auto till = from + 1; till != inside.size(); ++till) {
|
||||
if (!inside[till].isLetterOrNumber()) {
|
||||
return TextSelection{ uint16(from), uint16(till) };
|
||||
}
|
||||
}
|
||||
return TextSelection{ uint16(from), uint16(inside.size()) };
|
||||
};
|
||||
const auto words = Ui::Text::Words(lower);
|
||||
for (const auto &word : words) {
|
||||
const auto length = int(word.size());
|
||||
const auto cut = length / 2;
|
||||
const auto part = word.mid(0, length - cut);
|
||||
const auto offset = find(part);
|
||||
if (offset < 0) {
|
||||
continue;
|
||||
}
|
||||
for (auto i = 0; i != cut; ++i) {
|
||||
const auto part = word.mid(0, length - i);
|
||||
if (const auto from = find(part); from >= 0) {
|
||||
return tillEndOfWord(from);
|
||||
}
|
||||
}
|
||||
return tillEndOfWord(offset);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace HistoryView
|
||||
|
||||
@@ -20,6 +20,7 @@ struct HistoryMessageReply;
|
||||
struct PreparedServiceText;
|
||||
|
||||
namespace Data {
|
||||
class Thread;
|
||||
struct Reaction;
|
||||
struct ReactionId;
|
||||
} // namespace Data
|
||||
@@ -265,8 +266,10 @@ struct DateBadge : RuntimeComponent<DateBadge, Element> {
|
||||
|
||||
};
|
||||
|
||||
struct MonoforumSenderBar : RuntimeComponent<MonoforumSenderBar, Element> {
|
||||
void init(not_null<PeerData*> parentChat, not_null<PeerData*> peer);
|
||||
struct ForumThreadBar : RuntimeComponent<ForumThreadBar, Element> {
|
||||
void init(
|
||||
not_null<PeerData*> parentChat,
|
||||
not_null<Data::Thread*> thread);
|
||||
|
||||
int height() const;
|
||||
void paint(
|
||||
@@ -276,7 +279,7 @@ struct MonoforumSenderBar : RuntimeComponent<MonoforumSenderBar, Element> {
|
||||
int w,
|
||||
bool chatWide,
|
||||
bool skipPatternLine) const;
|
||||
static void PaintFor(
|
||||
static int PaintForGetWidth(
|
||||
Painter &p,
|
||||
not_null<const Ui::ChatStyle*> st,
|
||||
not_null<Element*> itemView,
|
||||
@@ -285,9 +288,8 @@ struct MonoforumSenderBar : RuntimeComponent<MonoforumSenderBar, Element> {
|
||||
int w,
|
||||
bool chatWide);
|
||||
|
||||
PeerData *sender = nullptr;
|
||||
base::weak_ptr<Data::Thread> thread;
|
||||
Ui::Text::String text;
|
||||
ClickHandlerPtr link;
|
||||
mutable Ui::PeerUserpicView view;
|
||||
int width = 0;
|
||||
|
||||
@@ -295,7 +297,7 @@ private:
|
||||
static void Paint(
|
||||
Painter &p,
|
||||
not_null<const Ui::ChatStyle*> st,
|
||||
not_null<PeerData*> sender,
|
||||
not_null<Data::Thread*> thread,
|
||||
const Ui::Text::String &text,
|
||||
int width,
|
||||
Ui::PeerUserpicView &view,
|
||||
@@ -475,7 +477,7 @@ public:
|
||||
[[nodiscard]] bool displayDate() const;
|
||||
[[nodiscard]] bool isInOneDayWithPrevious() const;
|
||||
|
||||
[[nodiscard]] bool displayMonoforumSender() const;
|
||||
[[nodiscard]] bool displayForumThreadBar() const;
|
||||
[[nodiscard]] bool isInOneBunchWithPrevious() const;
|
||||
|
||||
virtual void draw(Painter &p, const PaintContext &context) const = 0;
|
||||
@@ -688,7 +690,7 @@ protected:
|
||||
std::unique_ptr<Reactions::InlineList> _reactions;
|
||||
|
||||
private:
|
||||
void recountMonoforumSenderBarInBlocks();
|
||||
void recountThreadBarInBlocks();
|
||||
|
||||
// This should be called only from previousInBlocksChanged()
|
||||
// to add required bits to the Composer mask
|
||||
@@ -755,4 +757,12 @@ private:
|
||||
[[nodiscard]] Window::SessionController *ExtractController(
|
||||
const ClickContext &context);
|
||||
|
||||
[[nodiscard]] TextSelection FindSearchQueryHighlight(
|
||||
const QString &text,
|
||||
const QString &query);
|
||||
|
||||
[[nodiscard]] TextSelection FindSearchQueryHighlight(
|
||||
const QString &text,
|
||||
QStringView lower);
|
||||
|
||||
} // namespace HistoryView
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
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 "history/view/history_view_group_members_widget.h"
|
||||
|
||||
#include "boxes/peers/edit_participants_box.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
namespace HistoryView {
|
||||
namespace {
|
||||
|
||||
class GroupMembersWidgetController : public ParticipantsBoxController {
|
||||
public:
|
||||
using ParticipantsBoxController::ParticipantsBoxController;
|
||||
|
||||
protected:
|
||||
base::unique_qptr<Ui::PopupMenu> rowContextMenu(
|
||||
QWidget *parent,
|
||||
not_null<PeerListRow*> row) override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
GroupMembersWidget::GroupMembersWidget(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<PeerData*> peer)
|
||||
: Ui::RpWidget(parent)
|
||||
, _show(navigation->uiShow())
|
||||
, _listController(std::make_unique<GroupMembersWidgetController>(
|
||||
navigation,
|
||||
peer,
|
||||
ParticipantsBoxController::Role::Profile)) {
|
||||
_listController->setStoriesShown(true);
|
||||
setupList();
|
||||
setContent(_list.data());
|
||||
_listController->setDelegate(static_cast<PeerListDelegate*>(this));
|
||||
}
|
||||
|
||||
void GroupMembersWidget::setupList() {
|
||||
const auto topSkip = 0;
|
||||
_listController->setStyleOverrides(&st::groupMembersWidgetList);
|
||||
_listController->setStoriesShown(true);
|
||||
_list = object_ptr<PeerListContent>(this, _listController.get());
|
||||
widthValue() | rpl::start_with_next([this](int newWidth) {
|
||||
if (newWidth > 0) {
|
||||
_list->resizeToWidth(newWidth);
|
||||
}
|
||||
}, _list->lifetime());
|
||||
_list->heightValue() | rpl::start_with_next([=](int listHeight) {
|
||||
if (const auto newHeight = topSkip + listHeight; newHeight > 0) {
|
||||
resize(width(), newHeight);
|
||||
}
|
||||
}, _list->lifetime());
|
||||
_list->moveToLeft(0, topSkip);
|
||||
}
|
||||
|
||||
void GroupMembersWidget::peerListSetTitle(rpl::producer<QString> title) {
|
||||
}
|
||||
|
||||
void GroupMembersWidget::peerListSetAdditionalTitle(
|
||||
rpl::producer<QString> title) {
|
||||
}
|
||||
|
||||
bool GroupMembersWidget::peerListIsRowChecked(not_null<PeerListRow*> row) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int GroupMembersWidget::peerListSelectedRowsCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GroupMembersWidget::peerListScrollToTop() {
|
||||
}
|
||||
|
||||
void GroupMembersWidget::peerListAddSelectedPeerInBunch(
|
||||
not_null<PeerData*> peer) {
|
||||
Unexpected("Item selection in Info::Profile::Members.");
|
||||
}
|
||||
|
||||
void GroupMembersWidget::peerListAddSelectedRowInBunch(
|
||||
not_null<PeerListRow*> row) {
|
||||
Unexpected("Item selection in Info::Profile::Members.");
|
||||
}
|
||||
|
||||
void GroupMembersWidget::peerListFinishSelectedRowsBunch() {
|
||||
}
|
||||
|
||||
std::shared_ptr<Main::SessionShow> GroupMembersWidget::peerListUiShow() {
|
||||
return _show;
|
||||
}
|
||||
|
||||
void GroupMembersWidget::peerListSetDescription(
|
||||
object_ptr<Ui::FlatLabel> description) {
|
||||
description.destroy();
|
||||
}
|
||||
|
||||
void GroupMembersWidget::peerListShowRowMenu(
|
||||
not_null<PeerListRow*> row,
|
||||
bool highlightRow,
|
||||
Fn<void(not_null<Ui::PopupMenu*>)> destroyed) {
|
||||
}
|
||||
|
||||
} // namespace HistoryView
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "boxes/peer_list_box.h"
|
||||
|
||||
namespace Window {
|
||||
class SessionNavigation;
|
||||
} // namespace Window
|
||||
|
||||
class ParticipantsBoxController;
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
class GroupMembersWidget
|
||||
: public Ui::RpWidget
|
||||
, public PeerListContentDelegate {
|
||||
public:
|
||||
GroupMembersWidget(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<PeerData*> peer);
|
||||
|
||||
private:
|
||||
void setupList();
|
||||
|
||||
// PeerListContentDelegate interface.
|
||||
void peerListSetTitle(rpl::producer<QString> title) override;
|
||||
void peerListSetAdditionalTitle(rpl::producer<QString> title) override;
|
||||
bool peerListIsRowChecked(not_null<PeerListRow*> row) override;
|
||||
int peerListSelectedRowsCount() override;
|
||||
void peerListScrollToTop() override;
|
||||
void peerListAddSelectedPeerInBunch(
|
||||
not_null<PeerData*> peer) override;
|
||||
void peerListAddSelectedRowInBunch(
|
||||
not_null<PeerListRow*> row) override;
|
||||
void peerListFinishSelectedRowsBunch() override;
|
||||
void peerListSetDescription(
|
||||
object_ptr<Ui::FlatLabel> description) override;
|
||||
std::shared_ptr<Main::SessionShow> peerListUiShow() override;
|
||||
void peerListShowRowMenu(
|
||||
not_null<PeerListRow*> row,
|
||||
bool highlightRow,
|
||||
Fn<void(not_null<Ui::PopupMenu*>)> destroyed = nullptr) override;
|
||||
// PeerListContentDelegate interface.
|
||||
|
||||
std::shared_ptr<Main::SessionShow> _show;
|
||||
object_ptr<PeerListContent> _list = { nullptr };
|
||||
std::unique_ptr<ParticipantsBoxController> _listController;
|
||||
|
||||
};
|
||||
|
||||
} // namespace HistoryView
|
||||
@@ -41,6 +41,7 @@ struct ItemPreview {
|
||||
|
||||
struct ToPreviewOptions {
|
||||
const std::vector<ItemPreviewImage> *existing = nullptr;
|
||||
QStringView searchLowerText;
|
||||
bool hideSender = false;
|
||||
bool hideCaption = false;
|
||||
bool ignoreMessageText = false;
|
||||
|
||||
@@ -1092,10 +1092,13 @@ QSize Message::performCountOptimalSize() {
|
||||
|
||||
void Message::refreshTopicButton() {
|
||||
const auto item = data();
|
||||
if (isAttachedToPrevious()
|
||||
|| delegate()->elementHideTopicButton(this)) {
|
||||
if (isAttachedToPrevious() || delegate()->elementHideTopicButton(this)) {
|
||||
_topicButton = nullptr;
|
||||
} else if (const auto topic = item->topic()) {
|
||||
if (topic->channel()->useSubsectionTabs()) {
|
||||
_topicButton = nullptr;
|
||||
return;
|
||||
}
|
||||
if (!_topicButton) {
|
||||
_topicButton = std::make_unique<TopicButton>();
|
||||
}
|
||||
@@ -1132,8 +1135,8 @@ int Message::marginTop() const {
|
||||
if (const auto bar = Get<UnreadBar>()) {
|
||||
result += bar->height();
|
||||
}
|
||||
if (const auto monoforumBar = Get<MonoforumSenderBar>()) {
|
||||
result += monoforumBar->height();
|
||||
if (const auto bar = Get<ForumThreadBar>()) {
|
||||
result += bar->height();
|
||||
}
|
||||
if (const auto service = Get<ServicePreMessage>()) {
|
||||
result += service->height;
|
||||
@@ -1181,8 +1184,8 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
||||
if (const auto date = Get<DateBadge>()) {
|
||||
aboveh += date->height();
|
||||
}
|
||||
if (const auto sender = Get<MonoforumSenderBar>()) {
|
||||
aboveh += sender->height();
|
||||
if (const auto bar = Get<ForumThreadBar>()) {
|
||||
aboveh += bar->height();
|
||||
}
|
||||
if (context.clip.intersects(QRect(0, aboveh, width(), unreadbarh))) {
|
||||
p.translate(0, aboveh);
|
||||
|
||||
@@ -527,8 +527,8 @@ int Service::marginTop() const {
|
||||
if (const auto bar = Get<UnreadBar>()) {
|
||||
result += bar->height();
|
||||
}
|
||||
if (const auto monoforumBar = Get<MonoforumSenderBar>()) {
|
||||
result += monoforumBar->height();
|
||||
if (const auto bar = Get<ForumThreadBar>()) {
|
||||
result += bar->height();
|
||||
}
|
||||
if (const auto service = Get<ServicePreMessage>()) {
|
||||
result += service->height;
|
||||
@@ -553,8 +553,8 @@ void Service::draw(Painter &p, const PaintContext &context) const {
|
||||
if (const auto date = Get<DateBadge>()) {
|
||||
aboveh += date->height();
|
||||
}
|
||||
if (const auto sender = Get<MonoforumSenderBar>()) {
|
||||
aboveh += sender->height();
|
||||
if (const auto bar = Get<ForumThreadBar>()) {
|
||||
aboveh += bar->height();
|
||||
}
|
||||
if (context.clip.intersects(QRect(0, aboveh, width(), unreadbarh))) {
|
||||
p.translate(0, aboveh);
|
||||
|
||||
@@ -35,7 +35,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/label_with_custom_emoji.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/slider_natural_width.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
|
||||
@@ -446,13 +446,12 @@ object_ptr<Ui::BoxContent> StarRefLinkBox(
|
||||
CreateLinkIcon(box, &bot->session(), row.state.users),
|
||||
st::boxRowPadding + st::starrefJoinUserpicsPadding);
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_star_ref_link_title(),
|
||||
st::boxTitle)),
|
||||
st::boxRowPadding + st::starrefJoinTitlePadding);
|
||||
tr::lng_star_ref_link_title(),
|
||||
st::boxTitle),
|
||||
st::boxRowPadding + st::starrefJoinTitlePadding,
|
||||
style::al_top);
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
@@ -470,7 +469,6 @@ object_ptr<Ui::BoxContent> StarRefLinkBox(
|
||||
FormatForProgramDuration(program.durationMonths),
|
||||
Ui::Text::WithEntities),
|
||||
st::starrefCenteredText),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
|
||||
Ui::AddSkip(box->verticalLayout(), st::defaultVerticalListSkip * 3);
|
||||
@@ -480,7 +478,6 @@ object_ptr<Ui::BoxContent> StarRefLinkBox(
|
||||
box,
|
||||
tr::lng_star_ref_link_recipient(),
|
||||
st::starrefCenteredText),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
Ui::AddSkip(box->verticalLayout());
|
||||
box->addRow(object_ptr<Ui::AbstractButton>::fromRaw(
|
||||
@@ -579,13 +576,12 @@ object_ptr<Ui::BoxContent> JoinStarRefBox(
|
||||
}, box->lifetime());
|
||||
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_star_ref_title(),
|
||||
st::boxTitle)),
|
||||
st::boxRowPadding + st::starrefJoinTitlePadding);
|
||||
tr::lng_star_ref_title(),
|
||||
st::boxTitle),
|
||||
st::boxRowPadding + st::starrefJoinTitlePadding,
|
||||
style::al_top);
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
@@ -599,7 +595,6 @@ object_ptr<Ui::BoxContent> JoinStarRefBox(
|
||||
FormatForProgramDuration(program.durationMonths),
|
||||
Ui::Text::WithEntities),
|
||||
st::starrefCenteredText),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
|
||||
Ui::AddSkip(box->verticalLayout(), st::defaultVerticalListSkip * 3);
|
||||
@@ -641,7 +636,6 @@ object_ptr<Ui::BoxContent> JoinStarRefBox(
|
||||
box,
|
||||
tr::lng_star_ref_link_recipient(),
|
||||
st::starrefCenteredText),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
Ui::AddSkip(box->verticalLayout());
|
||||
const auto recipientWrap = box->addRow(
|
||||
|
||||
@@ -215,7 +215,7 @@ void AddPremiumTopBarWithDefaultTitleBar(
|
||||
+ st::defaultVerticalListSkip,
|
||||
st::boxDividerBg,
|
||||
RectPart::Bottom),
|
||||
{});
|
||||
style::margins());
|
||||
bar->setPaused(true);
|
||||
bar->setRoundEdges(false);
|
||||
bar->setMaximumHeight(st::giveawayGiftCodeTopHeight);
|
||||
@@ -348,12 +348,11 @@ void CreateGiveawayBox(
|
||||
Ui::AddSkip(container);
|
||||
Ui::AddSkip(container);
|
||||
container->add(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_contacts_loading(),
|
||||
st::giveawayLoadingLabel)));
|
||||
tr::lng_contacts_loading(),
|
||||
st::giveawayLoadingLabel),
|
||||
style::al_top);
|
||||
Ui::AddSkip(container);
|
||||
Ui::AddSkip(container);
|
||||
}
|
||||
|
||||
@@ -177,12 +177,11 @@ void SelectCountriesBox(
|
||||
Ui::AddSkip(container);
|
||||
Ui::AddSkip(container);
|
||||
container->add(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
tr::lng_search_messages_none(),
|
||||
st::membersAbout)));
|
||||
tr::lng_search_messages_none(),
|
||||
st::membersAbout),
|
||||
style::al_top);
|
||||
Ui::AddSkip(container);
|
||||
Ui::AddSkip(container);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/widgets/fields/input_field.h"
|
||||
#include "ui/widgets/label_with_custom_emoji.h"
|
||||
#include "ui/widgets/peer_bubble.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/widgets/slider_natural_width.h"
|
||||
@@ -141,14 +140,12 @@ void AddHeader(
|
||||
}
|
||||
|
||||
void AddRecipient(not_null<Ui::GenericBox*> box, const TextWithEntities &t) {
|
||||
const auto wrap = box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::RoundButton>>(
|
||||
const auto container = box->addRow(
|
||||
object_ptr<Ui::RoundButton>(
|
||||
box,
|
||||
object_ptr<Ui::RoundButton>(
|
||||
box,
|
||||
rpl::single(QString()),
|
||||
st::channelEarnHistoryRecipientButton)));
|
||||
const auto container = wrap->entity();
|
||||
rpl::single(QString()),
|
||||
st::channelEarnHistoryRecipientButton),
|
||||
style::al_top);
|
||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||
container,
|
||||
rpl::single(t),
|
||||
@@ -200,6 +197,8 @@ void AddRecipient(not_null<Ui::GenericBox*> box, const TextWithEntities &t) {
|
||||
QLocale().toString(date.time(), QLocale::ShortFormat));
|
||||
}
|
||||
|
||||
constexpr auto kMinus = QChar(0x2212);
|
||||
|
||||
} // namespace
|
||||
|
||||
InnerWidget::InnerWidget(
|
||||
@@ -401,7 +400,6 @@ void InnerWidget::fill() {
|
||||
);
|
||||
|
||||
constexpr auto kMinorLength = 3;
|
||||
constexpr auto kMinus = QChar(0x2212);
|
||||
//constexpr auto kApproximately = QChar(0x2248);
|
||||
const auto multiplier = data.usdRate;
|
||||
|
||||
@@ -412,51 +410,20 @@ void InnerWidget::fill() {
|
||||
|
||||
const auto session = &_peer->session();
|
||||
const auto withdrawalEnabled = WithdrawalEnabled(session);
|
||||
const auto addEmojiToMajor = [=](
|
||||
not_null<Ui::FlatLabel*> label,
|
||||
rpl::producer<CreditsAmount> value,
|
||||
std::optional<bool> isIn,
|
||||
std::optional<QMargins> margins) {
|
||||
const auto &st = label->st();
|
||||
const auto prepended = !isIn
|
||||
? TextWithEntities()
|
||||
: TextWithEntities::Simple((*isIn) ? QChar('+') : kMinus);
|
||||
std::move(
|
||||
value
|
||||
) | rpl::start_with_next([=](CreditsAmount v) {
|
||||
auto helper = Ui::Text::CustomEmojiHelper();
|
||||
auto icon = helper.paletteDependent({ .factory = [=] {
|
||||
return Ui::Earn::IconCurrencyColored(
|
||||
st.style.font,
|
||||
!isIn
|
||||
? st::currencyFg->c
|
||||
: (*isIn)
|
||||
? st::boxTextFgGood->c
|
||||
: st::menuIconAttentionColor->c);
|
||||
}, .margin = margins
|
||||
? *margins
|
||||
: st::channelEarnCurrencyCommonMargins });
|
||||
label->setMarkedText(
|
||||
base::duplicate(prepended).append(icon).append(MajorPart(v)),
|
||||
helper.context());
|
||||
}, label->lifetime());
|
||||
};
|
||||
|
||||
const auto arrow = Ui::Text::IconEmoji(&st::textMoreIconEmoji);
|
||||
const auto addAboutWithLearn = [&](const tr::phrase<lngtag_link> &text) {
|
||||
auto label = Ui::CreateLabelWithCustomEmoji(
|
||||
auto label = object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
text(
|
||||
lt_link,
|
||||
tr::lng_channel_earn_about_link(
|
||||
lt_emoji,
|
||||
rpl::single(arrow),
|
||||
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),
|
||||
Core::TextContext({ .session = session }),
|
||||
st::boxDividerLabel);
|
||||
label->setLink(1, std::make_shared<LambdaClickHandler>([=] {
|
||||
_show->showBox(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
@@ -480,10 +447,10 @@ void InnerWidget::fill() {
|
||||
const auto rect = Rect(icon.size() * 1.4);
|
||||
auto owned = object_ptr<Ui::RpWidget>(content);
|
||||
owned->resize(rect.size());
|
||||
owned->setNaturalWidth(rect.width());
|
||||
const auto widget = box->addRow(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
content,
|
||||
std::move(owned)))->entity();
|
||||
std::move(owned),
|
||||
style::al_top);
|
||||
widget->paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
auto p = Painter(widget);
|
||||
@@ -496,14 +463,13 @@ void InnerWidget::fill() {
|
||||
}
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
box->addRow(object_ptr<Ui::CenterWrap<>>(
|
||||
box->addRow(object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
bot
|
||||
? tr::lng_channel_earn_bot_learn_title()
|
||||
: tr::lng_channel_earn_learn_title(),
|
||||
st::boxTitle)));
|
||||
bot
|
||||
? tr::lng_channel_earn_bot_learn_title()
|
||||
: tr::lng_channel_earn_learn_title(),
|
||||
st::boxTitle),
|
||||
style::al_top);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
@@ -571,18 +537,17 @@ void InnerWidget::fill() {
|
||||
Ui::AddSkip(content);
|
||||
{
|
||||
const auto l = box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
Ui::CreateLabelWithCustomEmoji(
|
||||
content,
|
||||
tr::lng_channel_earn_learn_coin_title(
|
||||
lt_emoji,
|
||||
rpl::single(
|
||||
Ui::Text::Link(bigCurrencyIcon, 1)),
|
||||
Ui::Text::RichLangValue
|
||||
),
|
||||
emojiHelper.context(),
|
||||
st::boxTitle)))->entity();
|
||||
tr::lng_channel_earn_learn_coin_title(
|
||||
lt_emoji,
|
||||
rpl::single(
|
||||
Ui::Text::Link(bigCurrencyIcon, 1)),
|
||||
Ui::Text::RichLangValue),
|
||||
st::boxTitle,
|
||||
st::defaultPopupMenu,
|
||||
emojiHelper.context()),
|
||||
style::al_top);
|
||||
const auto diamonds = l->lifetime().make_state<int>(0);
|
||||
l->setLink(1, std::make_shared<LambdaClickHandler>([=] {
|
||||
const auto count = (*diamonds);
|
||||
@@ -598,20 +563,19 @@ void InnerWidget::fill() {
|
||||
Ui::AddSkip(content);
|
||||
{
|
||||
const auto label = box->addRow(
|
||||
Ui::CreateLabelWithCustomEmoji(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
tr::lng_channel_earn_learn_coin_about(
|
||||
lt_link,
|
||||
tr::lng_channel_earn_about_link(
|
||||
lt_emoji,
|
||||
rpl::single(arrow),
|
||||
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
|
||||
),
|
||||
Core::TextContext({ .session = session }),
|
||||
Ui::Text::RichLangValue),
|
||||
st::channelEarnLearnDescription));
|
||||
label->resizeToWidth(box->width()
|
||||
- rect::m::sum::h(st::boxRowPadding));
|
||||
@@ -727,7 +691,7 @@ void InnerWidget::fill() {
|
||||
const auto majorLabel = Ui::CreateChild<Ui::FlatLabel>(
|
||||
line,
|
||||
st::channelEarnOverviewMajorLabel);
|
||||
addEmojiToMajor(
|
||||
AddEmojiToMajor(
|
||||
majorLabel,
|
||||
rpl::duplicate(currencyValue),
|
||||
{},
|
||||
@@ -870,9 +834,8 @@ void InnerWidget::fill() {
|
||||
Ui::AddSkip(container);
|
||||
|
||||
const auto labels = container->add(
|
||||
object_ptr<Ui::CenterWrap<Ui::RpWidget>>(
|
||||
container,
|
||||
object_ptr<Ui::RpWidget>(container)))->entity();
|
||||
object_ptr<Ui::RpWidget>(container),
|
||||
style::al_top);
|
||||
|
||||
const auto majorLabel = Ui::CreateChild<Ui::FlatLabel>(
|
||||
labels,
|
||||
@@ -880,7 +843,7 @@ void InnerWidget::fill() {
|
||||
{
|
||||
const auto &m = st::channelEarnCurrencyCommonMargins;
|
||||
const auto p = QMargins(m.left(), 0, m.right(), m.bottom());
|
||||
addEmojiToMajor(majorLabel, rpl::single(value), {}, p);
|
||||
AddEmojiToMajor(majorLabel, rpl::single(value), {}, p);
|
||||
}
|
||||
majorLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
const auto minorLabel = Ui::CreateChild<Ui::FlatLabel>(
|
||||
@@ -897,6 +860,8 @@ void InnerWidget::fill() {
|
||||
labels->resize(
|
||||
majorSize.width() + minorSize.width(),
|
||||
majorSize.height());
|
||||
labels->setNaturalWidth(
|
||||
majorSize.width() + minorSize.width());
|
||||
majorLabel->moveToLeft(0, 0);
|
||||
minorLabel->moveToRight(
|
||||
0,
|
||||
@@ -906,12 +871,11 @@ void InnerWidget::fill() {
|
||||
|
||||
Ui::AddSkip(container);
|
||||
container->add(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
ToUsd(value, multiplier, 0),
|
||||
st::channelEarnOverviewSubMinorLabel)));
|
||||
ToUsd(value, multiplier, 0),
|
||||
st::channelEarnOverviewSubMinorLabel),
|
||||
style::al_top);
|
||||
|
||||
Ui::AddSkip(container);
|
||||
|
||||
@@ -921,7 +885,8 @@ void InnerWidget::fill() {
|
||||
container,
|
||||
rpl::never<QString>(),
|
||||
stButton),
|
||||
st::boxRowPadding);
|
||||
st::boxRowPadding,
|
||||
style::al_justify);
|
||||
|
||||
const auto label = Ui::CreateChild<Ui::FlatLabel>(
|
||||
button,
|
||||
@@ -1162,7 +1127,7 @@ void InnerWidget::fill() {
|
||||
const auto majorLabel = Ui::CreateChild<Ui::FlatLabel>(
|
||||
wrap,
|
||||
st::channelEarnHistoryMajorLabel);
|
||||
addEmojiToMajor(
|
||||
AddEmojiToMajor(
|
||||
majorLabel,
|
||||
rpl::single(entry.credits),
|
||||
isIn,
|
||||
@@ -1189,14 +1154,13 @@ void InnerWidget::fill() {
|
||||
Ui::AddSkip(box->verticalLayout());
|
||||
Ui::AddSkip(box->verticalLayout());
|
||||
const auto labels = box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::RpWidget>>(
|
||||
box,
|
||||
object_ptr<Ui::RpWidget>(box)))->entity();
|
||||
object_ptr<Ui::RpWidget>(box),
|
||||
style::al_top);
|
||||
|
||||
const auto majorLabel = Ui::CreateChild<Ui::FlatLabel>(
|
||||
labels,
|
||||
st::channelEarnOverviewMajorLabel);
|
||||
addEmojiToMajor(
|
||||
AddEmojiToMajor(
|
||||
majorLabel,
|
||||
rpl::single(entry.credits),
|
||||
isIn,
|
||||
@@ -1220,6 +1184,8 @@ void InnerWidget::fill() {
|
||||
labels->resize(
|
||||
majorSize.width() + minorSize.width(),
|
||||
majorSize.height());
|
||||
labels->setNaturalWidth(
|
||||
majorSize.width() + minorSize.width());
|
||||
majorLabel->moveToLeft(0, 0);
|
||||
minorLabel->moveToRight(
|
||||
0,
|
||||
@@ -1227,12 +1193,12 @@ void InnerWidget::fill() {
|
||||
}, box->lifetime());
|
||||
|
||||
Ui::AddSkip(box->verticalLayout());
|
||||
box->addRow(object_ptr<Ui::CenterWrap<>>(
|
||||
box,
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
dateText,
|
||||
st::channelEarnHistorySubLabel)));
|
||||
st::channelEarnHistorySubLabel),
|
||||
style::al_top);
|
||||
Ui::AddSkip(box->verticalLayout());
|
||||
Ui::AddSkip(box->verticalLayout());
|
||||
AddChannelEarnTable(
|
||||
@@ -1241,14 +1207,14 @@ void InnerWidget::fill() {
|
||||
entry);
|
||||
Ui::AddSkip(box->verticalLayout());
|
||||
Ui::AddSkip(box->verticalLayout());
|
||||
box->addRow(object_ptr<Ui::CenterWrap<>>(
|
||||
box,
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
isIn
|
||||
? tr::lng_channel_earn_history_in_about()
|
||||
: tr::lng_channel_earn_history_out(),
|
||||
st::channelEarnHistoryDescriptionLabel)));
|
||||
st::channelEarnHistoryDescriptionLabel),
|
||||
style::al_top);
|
||||
Ui::AddSkip(box->verticalLayout());
|
||||
if (isIn) {
|
||||
Ui::AddSkip(box->verticalLayout());
|
||||
@@ -1259,9 +1225,8 @@ void InnerWidget::fill() {
|
||||
}
|
||||
if (isIn) {
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
box,
|
||||
Ui::CreatePeerBubble(box, peer)));
|
||||
Ui::CreatePeerBubble(box, peer),
|
||||
style::al_top);
|
||||
}
|
||||
const auto closeBox = [=] { box->closeBox(); };
|
||||
{
|
||||
@@ -1530,4 +1495,34 @@ not_null<PeerData*> InnerWidget::peer() const {
|
||||
return _peer;
|
||||
}
|
||||
|
||||
void AddEmojiToMajor(
|
||||
not_null<Ui::FlatLabel*> label,
|
||||
rpl::producer<CreditsAmount> value,
|
||||
std::optional<bool> isIn,
|
||||
std::optional<QMargins> margins) {
|
||||
const auto &st = label->st();
|
||||
const auto prepended = !isIn
|
||||
? TextWithEntities()
|
||||
: TextWithEntities::Simple((*isIn) ? QChar('+') : kMinus);
|
||||
std::move(
|
||||
value
|
||||
) | rpl::start_with_next([=](CreditsAmount v) {
|
||||
auto helper = Ui::Text::CustomEmojiHelper();
|
||||
auto icon = helper.paletteDependent({ .factory = [=] {
|
||||
return Ui::Earn::IconCurrencyColored(
|
||||
st.style.font,
|
||||
!isIn
|
||||
? st::currencyFg->c
|
||||
: (*isIn)
|
||||
? st::boxTextFgGood->c
|
||||
: st::menuIconAttentionColor->c);
|
||||
}, .margin = margins
|
||||
? *margins
|
||||
: st::channelEarnCurrencyCommonMargins });
|
||||
label->setMarkedText(
|
||||
base::duplicate(prepended).append(icon).append(MajorPart(v)),
|
||||
helper.context());
|
||||
}, label->lifetime());
|
||||
}
|
||||
|
||||
} // namespace Info::ChannelEarn
|
||||
|
||||
@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace Ui {
|
||||
class Show;
|
||||
class FlatLabel;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Info {
|
||||
@@ -63,4 +64,10 @@ private:
|
||||
|
||||
};
|
||||
|
||||
void AddEmojiToMajor(
|
||||
not_null<Ui::FlatLabel*> label,
|
||||
rpl::producer<CreditsAmount> value,
|
||||
std::optional<bool> isIn,
|
||||
std::optional<QMargins> margins);
|
||||
|
||||
} // namespace Info::ChannelEarn
|
||||
|
||||
@@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "info/channel_statistics/earn/earn_icons.h"
|
||||
#include "main/main_session.h"
|
||||
#include "overview/overview_checkbox.h"
|
||||
#include "settings/settings_credits_graphics.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/text/format_values.h"
|
||||
@@ -34,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_credits.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_overview.h"
|
||||
#include "styles/style_premium.h"
|
||||
|
||||
namespace Info::PeerGifts {
|
||||
@@ -78,6 +80,18 @@ GiftButton::~GiftButton() {
|
||||
unsubscribe();
|
||||
}
|
||||
|
||||
void GiftButton::onStateChanged(State was, StateChangeSource source) {
|
||||
if (_check) {
|
||||
const auto diff = state() ^ was;
|
||||
if (diff & State::Enum::Over) {
|
||||
_check->setActive(state() & State::Enum::Over);
|
||||
}
|
||||
if (diff & State::Enum::Down) {
|
||||
_check->setPressed(state() & State::Enum::Down);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GiftButton::unsubscribe() {
|
||||
if (base::take(_subscribed)) {
|
||||
_userpic->subscribeToUpdates(nullptr);
|
||||
@@ -85,6 +99,16 @@ void GiftButton::unsubscribe() {
|
||||
}
|
||||
|
||||
void GiftButton::setDescriptor(const GiftDescriptor &descriptor, Mode mode) {
|
||||
_mode = mode;
|
||||
|
||||
if (_mode != GiftButtonMode::Selection) {
|
||||
_check = nullptr;
|
||||
} else if (!_check) {
|
||||
_check = std::make_unique<Overview::Layout::Checkbox>(
|
||||
[=] { update(); },
|
||||
st::overviewSmallCheck);
|
||||
}
|
||||
|
||||
const auto unique = v::is<GiftTypeStars>(descriptor)
|
||||
? v::get<GiftTypeStars>(descriptor).info.unique.get()
|
||||
: nullptr;
|
||||
@@ -106,7 +130,6 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor, Mode mode) {
|
||||
_descriptor = descriptor;
|
||||
_resalePrice = resalePrice;
|
||||
const auto resale = (_resalePrice > 0);
|
||||
_small = (mode != Mode::Full);
|
||||
v::match(descriptor, [&](const GiftTypePremium &data) {
|
||||
const auto months = data.months;
|
||||
_text = Ui::Text::String(st::giftBoxGiftHeight / 4);
|
||||
@@ -147,12 +170,12 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor, Mode mode) {
|
||||
const auto soldOut = data.info.limitedCount
|
||||
&& !data.userpic
|
||||
&& !data.info.limitedLeft;
|
||||
_userpic = !data.userpic
|
||||
_userpic = (!data.userpic || _mode == GiftButtonMode::Selection)
|
||||
? nullptr
|
||||
: data.from
|
||||
? Ui::MakeUserpicThumbnail(data.from)
|
||||
: Ui::MakeHiddenAuthorThumbnail();
|
||||
if (_small && !resale) {
|
||||
if (small() && !resale) {
|
||||
_price = {};
|
||||
_stars.reset();
|
||||
return;
|
||||
@@ -169,7 +192,7 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor, Mode mode) {
|
||||
? unique->starsForResale
|
||||
: data.info.starsResellMin)
|
||||
).append(data.info.resellCount > 1 ? "+" : ""))
|
||||
: (_small && unique && unique->starsForResale)
|
||||
: (small() && unique && unique->starsForResale)
|
||||
? Data::FormatGiftResaleAsked(*unique)
|
||||
: unique
|
||||
? tr::lng_gift_transfer_button(
|
||||
@@ -214,7 +237,7 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor, Mode mode) {
|
||||
_uniquePatternEmoji = nullptr;
|
||||
_uniquePatternCache.clear();
|
||||
|
||||
if (_small && !resale) {
|
||||
if (small() && !resale) {
|
||||
_button = QRect();
|
||||
return;
|
||||
}
|
||||
@@ -225,7 +248,7 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor, Mode mode) {
|
||||
QSize(buttonw, buttonh)
|
||||
).marginsAdded(st::giftBoxButtonPadding);
|
||||
const auto skipy = _delegate->buttonSize().height()
|
||||
- (_small
|
||||
- (small()
|
||||
? st::giftBoxButtonBottomSmall
|
||||
: _byStars.isEmpty()
|
||||
? st::giftBoxButtonBottom
|
||||
@@ -295,11 +318,17 @@ void GiftButton::setGeometry(QRect inner, QMargins extend) {
|
||||
}
|
||||
|
||||
QMargins GiftButton::currentExtend() const {
|
||||
const auto progress = _selectedAnimation.value(_selected ? 1. : 0.);
|
||||
const auto progress = (_mode == Mode::Selection)
|
||||
? 0.
|
||||
: _selectedAnimation.value(_selected ? 1. : 0.);
|
||||
const auto added = anim::interpolate(0, st::giftBoxSelectSkip, progress);
|
||||
return _extend + QMargins(added, added, added, added);
|
||||
}
|
||||
|
||||
bool GiftButton::small() const {
|
||||
return _mode != GiftButtonMode::Full;
|
||||
}
|
||||
|
||||
void GiftButton::toggleSelected(bool selected, anim::type animated) {
|
||||
if (_selected == selected) {
|
||||
if (animated == anim::type::instant) {
|
||||
@@ -310,8 +339,14 @@ void GiftButton::toggleSelected(bool selected, anim::type animated) {
|
||||
const auto duration = st::defaultRoundCheckbox.duration;
|
||||
_selected = selected;
|
||||
if (animated == anim::type::instant) {
|
||||
if (_check) {
|
||||
_check->finishAnimating();
|
||||
}
|
||||
_selectedAnimation.stop();
|
||||
return;
|
||||
} else if (_check) {
|
||||
_check->setChecked(selected, animated);
|
||||
return;
|
||||
}
|
||||
_selectedAnimation.start([=] {
|
||||
update();
|
||||
@@ -358,7 +393,9 @@ void GiftButton::paintBackground(QPainter &p, const QImage &background) {
|
||||
}
|
||||
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
const auto progress = _selectedAnimation.value(_selected ? 1. : 0.);
|
||||
const auto progress = (_mode == Mode::Selection)
|
||||
? 0.
|
||||
: _selectedAnimation.value(_selected ? 1. : 0.);
|
||||
if (progress < 0.01) {
|
||||
return;
|
||||
}
|
||||
@@ -446,7 +483,7 @@ void GiftButton::paintEvent(QPaintEvent *e) {
|
||||
const auto unique = v::is<GiftTypeStars>(_descriptor)
|
||||
? v::get<GiftTypeStars>(_descriptor).info.unique.get()
|
||||
: nullptr;
|
||||
const auto onsale = (unique && unique->starsForResale && _small);
|
||||
const auto onsale = unique && unique->starsForResale && small();
|
||||
const auto requirePremium = v::is<GiftTypeStars>(_descriptor)
|
||||
&& !v::get<GiftTypeStars>(_descriptor).userpic
|
||||
&& !v::get<GiftTypeStars>(_descriptor).info.unique
|
||||
@@ -482,6 +519,14 @@ void GiftButton::paintEvent(QPaintEvent *e) {
|
||||
const auto image = _userpic->image(st::giftBoxUserpicSize);
|
||||
const auto skip = st::giftBoxUserpicSkip;
|
||||
p.drawImage(extend.left() + skip, extend.top() + skip, image);
|
||||
} else if (_check) {
|
||||
const auto skip = st::giftBoxUserpicSkip;
|
||||
_check->paint(
|
||||
p,
|
||||
QPoint(extend.left() + skip, extend.top() + skip),
|
||||
width,
|
||||
_selected,
|
||||
true);
|
||||
}
|
||||
|
||||
auto frame = QImage();
|
||||
@@ -502,7 +547,7 @@ void GiftButton::paintEvent(QPaintEvent *e) {
|
||||
p.drawImage(
|
||||
QRect(
|
||||
(width - size.width()) / 2,
|
||||
(_small
|
||||
(small()
|
||||
? st::giftBoxSmallStickerTop
|
||||
: _text.isEmpty()
|
||||
? st::giftBoxStickerStarTop
|
||||
@@ -516,7 +561,7 @@ void GiftButton::paintEvent(QPaintEvent *e) {
|
||||
if (hidden) {
|
||||
const auto topleft = QPoint(
|
||||
(width - st::giftBoxStickerSize.width()) / 2,
|
||||
(_small
|
||||
(small()
|
||||
? st::giftBoxSmallStickerTop
|
||||
: _text.isEmpty()
|
||||
? st::giftBoxStickerStarTop
|
||||
@@ -781,7 +826,7 @@ QSize Delegate::buttonSize() {
|
||||
const auto available = width - padding.left() - padding.right();
|
||||
const auto singlew = (available - 2 * st::giftBoxGiftSkip.x())
|
||||
/ kGiftsPerRow;
|
||||
const auto minimal = (_mode == GiftButtonMode::Minimal);
|
||||
const auto minimal = (_mode != GiftButtonMode::Full);
|
||||
_single = QSize(
|
||||
singlew,
|
||||
minimal ? st::giftBoxGiftSmall : st::giftBoxGiftHeight);
|
||||
|
||||
@@ -34,6 +34,10 @@ namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Overview::Layout {
|
||||
class Checkbox;
|
||||
} // namespace Overview::Layout
|
||||
|
||||
namespace Ui {
|
||||
class DynamicImage;
|
||||
} // namespace Ui
|
||||
@@ -118,9 +122,10 @@ struct GiftBadge {
|
||||
const GiftBadge &) = default;
|
||||
};
|
||||
|
||||
enum class GiftButtonMode {
|
||||
enum class GiftButtonMode : uint8 {
|
||||
Full,
|
||||
Minimal,
|
||||
Selection,
|
||||
};
|
||||
|
||||
class GiftButtonDelegate {
|
||||
@@ -173,7 +178,9 @@ private:
|
||||
|
||||
void setDocument(not_null<DocumentData*> document);
|
||||
[[nodiscard]] QMargins currentExtend() const;
|
||||
[[nodiscard]] bool small() const;
|
||||
|
||||
void onStateChanged(State was, StateChangeSource source) override;
|
||||
void unsubscribe();
|
||||
|
||||
const not_null<GiftButtonDelegate*> _delegate;
|
||||
@@ -190,11 +197,12 @@ private:
|
||||
base::flat_map<float64, QImage> _uniquePatternCache;
|
||||
std::optional<Ui::Premium::ColoredMiniStars> _stars;
|
||||
Ui::Animations::Simple _selectedAnimation;
|
||||
std::unique_ptr<Overview::Layout::Checkbox> _check;
|
||||
int _resalePrice = 0;
|
||||
GiftButtonMode _mode = GiftButtonMode::Full;
|
||||
bool _subscribed = false;
|
||||
bool _patterned = false;
|
||||
bool _selected = false;
|
||||
bool _small = false;
|
||||
|
||||
QRect _button;
|
||||
QMargins _extend;
|
||||
|
||||
@@ -201,6 +201,7 @@ private:
|
||||
const not_null<Window::SessionController*> _window;
|
||||
const not_null<PeerData*> _peer;
|
||||
const int _addingToCollectionId = 0;
|
||||
const GiftButtonMode _mode;
|
||||
|
||||
rpl::variable<Descriptor> _descriptor;
|
||||
Delegate _delegate;
|
||||
@@ -267,8 +268,11 @@ InnerWidget::InnerWidget(
|
||||
, _window(window)
|
||||
, _peer(peer)
|
||||
, _addingToCollectionId(addingToCollectionId)
|
||||
, _mode(_addingToCollectionId
|
||||
? GiftButtonMode::Selection
|
||||
: GiftButtonMode::Minimal)
|
||||
, _descriptor(std::move(descriptor))
|
||||
, _delegate(&_window->session(), GiftButtonMode::Minimal)
|
||||
, _delegate(&_window->session(), _mode)
|
||||
, _all(std::move(all))
|
||||
, _entries(&_all)
|
||||
, _list(&_entries->list)
|
||||
@@ -750,7 +754,7 @@ void InnerWidget::validateButtons() {
|
||||
view.button->toggleSelected(
|
||||
_addingToCollectionId && _inCollection.contains(manageId),
|
||||
anim::type::instant);
|
||||
view.button->setDescriptor(descriptor, GiftButton::Mode::Minimal);
|
||||
view.button->setDescriptor(descriptor, _mode);
|
||||
view.button->setClickedCallback(callback);
|
||||
return true;
|
||||
};
|
||||
@@ -979,30 +983,27 @@ void InnerWidget::refreshAbout() {
|
||||
} else if (collectionCanAdd) {
|
||||
auto about = std::make_unique<Ui::VerticalLayout>(this);
|
||||
about->add(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
about.get(),
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
about.get(),
|
||||
tr::lng_gift_collection_empty_title(),
|
||||
st::collectionEmptyTitle)),
|
||||
st::collectionEmptyTitleMargin);
|
||||
tr::lng_gift_collection_empty_title(),
|
||||
st::collectionEmptyTitle),
|
||||
st::collectionEmptyTitleMargin,
|
||||
style::al_top);
|
||||
about->add(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
about.get(),
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
about.get(),
|
||||
tr::lng_gift_collection_empty_text(),
|
||||
st::collectionEmptyText)),
|
||||
st::collectionEmptyTextMargin);
|
||||
tr::lng_gift_collection_empty_text(),
|
||||
st::collectionEmptyText),
|
||||
st::collectionEmptyTextMargin,
|
||||
style::al_top);
|
||||
|
||||
const auto button = about->add(
|
||||
object_ptr<Ui::CenterWrap<Ui::RoundButton>>(
|
||||
object_ptr<Ui::RoundButton>(
|
||||
about.get(),
|
||||
object_ptr<Ui::RoundButton>(
|
||||
about.get(),
|
||||
rpl::single(QString()),
|
||||
st::collectionEmptyButton)),
|
||||
st::collectionEmptyAddMargin)->entity();
|
||||
rpl::single(QString()),
|
||||
st::collectionEmptyButton),
|
||||
st::collectionEmptyAddMargin,
|
||||
style::al_top);
|
||||
button->setText(tr::lng_gift_collection_add_button(
|
||||
) | rpl::map([](const QString &text) {
|
||||
return Ui::Text::IconEmoji(&st::collectionAddIcon).append(text);
|
||||
@@ -1081,7 +1082,7 @@ void InnerWidget::editCollectionGifts(int id) {
|
||||
state->descriptor.value(),
|
||||
id,
|
||||
(_all.filter == Filter()) ? _all : Entries()),
|
||||
{});
|
||||
style::margins());
|
||||
state->changes = content->changes();
|
||||
|
||||
content->descriptorChanges(
|
||||
@@ -1505,6 +1506,7 @@ Widget::Widget(QWidget *parent, not_null<Controller*> controller)
|
||||
controller->parentController(),
|
||||
controller->giftsPeer(),
|
||||
_descriptor.value()));
|
||||
_emptyCollectionShown = _inner->collectionEmptyValue();
|
||||
_inner->notifyEnabled(
|
||||
) | rpl::take(1) | rpl::start_with_next([=](bool enabled) {
|
||||
_notifyEnabled = enabled;
|
||||
@@ -1518,7 +1520,10 @@ Widget::Widget(QWidget *parent, not_null<Controller*> controller)
|
||||
scrollTo({ 0, 0 });
|
||||
}, _inner->lifetime());
|
||||
|
||||
_descriptor.value() | rpl::start_with_next([=] {
|
||||
rpl::combine(
|
||||
_descriptor.value(),
|
||||
_emptyCollectionShown.value()
|
||||
) | rpl::start_with_next([=] {
|
||||
refreshBottom();
|
||||
}, _inner->lifetime());
|
||||
}
|
||||
@@ -1527,22 +1532,22 @@ void Widget::refreshBottom() {
|
||||
const auto notify = _notifyEnabled.has_value();
|
||||
const auto descriptor = _descriptor.current();
|
||||
const auto shownId = descriptor.collectionId;
|
||||
const auto withButton = shownId && peer()->canManageGifts();
|
||||
const auto withButton = shownId
|
||||
&& peer()->canManageGifts()
|
||||
&& !_emptyCollectionShown.current();
|
||||
const auto wasBottom = _pinnedToBottom ? _pinnedToBottom->height() : 0;
|
||||
delete _pinnedToBottom.data();
|
||||
if (!notify && !withButton) {
|
||||
setScrollBottomSkip(0);
|
||||
_hasPinnedToBottom = false;
|
||||
} else if (withButton) {
|
||||
setupBottomButton(wasBottom, _inner->collectionEmptyValue());
|
||||
setupBottomButton(wasBottom);
|
||||
} else {
|
||||
setupNotifyCheckbox(wasBottom, *_notifyEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::setupBottomButton(
|
||||
int wasBottomHeight,
|
||||
rpl::producer<bool> hidden) {
|
||||
void Widget::setupBottomButton(int wasBottomHeight) {
|
||||
_pinnedToBottom = Ui::CreateChild<Ui::SlideWrap<Ui::RpWidget>>(
|
||||
this,
|
||||
object_ptr<Ui::RpWidget>(this));
|
||||
@@ -1561,10 +1566,8 @@ void Widget::setupBottomButton(
|
||||
) | rpl::map([](const QString &text) {
|
||||
return Ui::Text::IconEmoji(&st::collectionAddIcon).append(text);
|
||||
}));
|
||||
std::move(hidden) | rpl::start_with_next([=](bool hidden) {
|
||||
button->setVisible(!hidden);
|
||||
_hasPinnedToBottom = !hidden;
|
||||
}, button->lifetime());
|
||||
button->show();
|
||||
_hasPinnedToBottom = true;
|
||||
|
||||
button->setClickedCallback([=] {
|
||||
if (const auto id = _descriptor.current().collectionId) {
|
||||
|
||||
@@ -105,12 +105,13 @@ private:
|
||||
std::shared_ptr<ContentMemento> doCreateMemento() override;
|
||||
|
||||
void setupNotifyCheckbox(int wasBottomHeight, bool enabled);
|
||||
void setupBottomButton(int wasBottomHeight, rpl::producer<bool> hidden);
|
||||
void setupBottomButton(int wasBottomHeight);
|
||||
void refreshBottom();
|
||||
|
||||
InnerWidget *_inner = nullptr;
|
||||
QPointer<Ui::SlideWrap<Ui::RpWidget>> _pinnedToBottom;
|
||||
rpl::variable<bool> _hasPinnedToBottom;
|
||||
rpl::variable<bool> _emptyCollectionShown;
|
||||
rpl::variable<Descriptor> _descriptor;
|
||||
std::optional<bool> _notifyEnabled;
|
||||
bool _shown = false;
|
||||
|
||||
@@ -1985,7 +1985,8 @@ void DetailsFiller::setupMainApp() {
|
||||
_wrap,
|
||||
tr::lng_profile_open_app(),
|
||||
st::infoOpenApp),
|
||||
st::infoOpenAppMargin);
|
||||
st::infoOpenAppMargin,
|
||||
style::al_justify);
|
||||
button->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
|
||||
|
||||
const auto user = _peer->asUser();
|
||||
|
||||
@@ -590,30 +590,29 @@ void FillLoading(
|
||||
content->add(std::move(icon.widget));
|
||||
|
||||
content->add(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
(type == LoadingType::Boosts)
|
||||
? tr::lng_stats_boosts_loading()
|
||||
: (type == LoadingType::Earn)
|
||||
? tr::lng_stats_earn_loading()
|
||||
: tr::lng_stats_loading(),
|
||||
st::changePhoneTitle)),
|
||||
st::changePhoneTitlePadding + st::boxRowPadding);
|
||||
(type == LoadingType::Boosts)
|
||||
? tr::lng_stats_boosts_loading()
|
||||
: (type == LoadingType::Earn)
|
||||
? tr::lng_stats_earn_loading()
|
||||
: tr::lng_stats_loading(),
|
||||
st::changePhoneTitle),
|
||||
st::changePhoneTitlePadding + st::boxRowPadding,
|
||||
style::al_top);
|
||||
|
||||
content->add(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
(type == LoadingType::Boosts)
|
||||
? tr::lng_stats_boosts_loading_subtext()
|
||||
: (type == LoadingType::Earn)
|
||||
? tr::lng_stats_earn_loading_subtext()
|
||||
: tr::lng_stats_loading_subtext(),
|
||||
st::statisticsLoadingSubtext)),
|
||||
st::changePhoneDescriptionPadding + st::boxRowPadding);
|
||||
(type == LoadingType::Boosts)
|
||||
? tr::lng_stats_boosts_loading_subtext()
|
||||
: (type == LoadingType::Earn)
|
||||
? tr::lng_stats_earn_loading_subtext()
|
||||
: tr::lng_stats_loading_subtext(),
|
||||
st::statisticsLoadingSubtext),
|
||||
st::changePhoneDescriptionPadding + st::boxRowPadding,
|
||||
style::al_top
|
||||
)->setTryMakeSimilarLines(true);
|
||||
|
||||
Ui::AddSkip(content, st::settingsBlockedListIconPadding.top());
|
||||
}
|
||||
|
||||
@@ -586,30 +586,27 @@ void InnerWidget::refreshEmpty() {
|
||||
if (albumCanAdd) {
|
||||
auto empty = object_ptr<Ui::VerticalLayout>(this);
|
||||
empty->add(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
empty.get(),
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
empty.get(),
|
||||
tr::lng_stories_album_empty_title(),
|
||||
st::collectionEmptyTitle)),
|
||||
st::collectionEmptyTitleMargin);
|
||||
tr::lng_stories_album_empty_title(),
|
||||
st::collectionEmptyTitle),
|
||||
st::collectionEmptyTitleMargin,
|
||||
style::al_top);
|
||||
empty->add(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
empty.get(),
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
empty.get(),
|
||||
tr::lng_stories_album_empty_text(),
|
||||
st::collectionEmptyText)),
|
||||
st::collectionEmptyTextMargin);
|
||||
tr::lng_stories_album_empty_text(),
|
||||
st::collectionEmptyText),
|
||||
st::collectionEmptyTextMargin,
|
||||
style::al_top);
|
||||
|
||||
const auto button = empty->add(
|
||||
object_ptr<Ui::CenterWrap<Ui::RoundButton>>(
|
||||
object_ptr<Ui::RoundButton>(
|
||||
empty.get(),
|
||||
object_ptr<Ui::RoundButton>(
|
||||
empty.get(),
|
||||
rpl::single(QString()),
|
||||
st::collectionEmptyButton)),
|
||||
st::collectionEmptyAddMargin)->entity();
|
||||
rpl::single(QString()),
|
||||
st::collectionEmptyButton),
|
||||
st::collectionEmptyAddMargin,
|
||||
style::al_top);
|
||||
button->setText(tr::lng_stories_album_add_button(
|
||||
) | rpl::map([](const QString &text) {
|
||||
return Ui::Text::IconEmoji(&st::collectionAddIcon).append(text);
|
||||
|
||||
@@ -65,6 +65,7 @@ Widget::Widget(
|
||||
controller,
|
||||
_albumId.value(),
|
||||
controller->key().storiesAddToAlbumId()));
|
||||
_emptyAlbumShown = _inner->albumEmptyValue();
|
||||
_inner->albumIdChanges() | rpl::start_with_next([=](int id) {
|
||||
controller->showSection(
|
||||
Make(controller->storiesPeer(), id),
|
||||
@@ -76,7 +77,10 @@ Widget::Widget(
|
||||
scrollTo(request);
|
||||
}, _inner->lifetime());
|
||||
|
||||
_albumId.value() | rpl::start_with_next([=] {
|
||||
rpl::combine(
|
||||
_albumId.value(),
|
||||
_emptyAlbumShown.value()
|
||||
) | rpl::start_with_next([=] {
|
||||
refreshBottom();
|
||||
}, _inner->lifetime());
|
||||
}
|
||||
@@ -133,20 +137,19 @@ void Widget::refreshBottom() {
|
||||
const auto albumId = _albumId.current();
|
||||
const auto withButton = (albumId != Data::kStoriesAlbumIdSaved)
|
||||
&& (albumId != Data::kStoriesAlbumIdArchive)
|
||||
&& controller()->storiesPeer()->canEditStories();
|
||||
&& controller()->storiesPeer()->canEditStories()
|
||||
&& !_emptyAlbumShown.current();
|
||||
const auto wasBottom = _pinnedToBottom ? _pinnedToBottom->height() : 0;
|
||||
delete _pinnedToBottom.data();
|
||||
if (!withButton) {
|
||||
setScrollBottomSkip(0);
|
||||
_hasPinnedToBottom = false;
|
||||
} else {
|
||||
setupBottomButton(wasBottom, _inner->albumEmptyValue());
|
||||
setupBottomButton(wasBottom);
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::setupBottomButton(
|
||||
int wasBottomHeight,
|
||||
rpl::producer<bool> hidden) {
|
||||
void Widget::setupBottomButton(int wasBottomHeight) {
|
||||
_pinnedToBottom = Ui::CreateChild<Ui::SlideWrap<Ui::RpWidget>>(
|
||||
this,
|
||||
object_ptr<Ui::RpWidget>(this));
|
||||
@@ -166,10 +169,7 @@ void Widget::setupBottomButton(
|
||||
return Ui::Text::IconEmoji(&st::collectionAddIcon).append(text);
|
||||
}));
|
||||
button->show();
|
||||
std::move(hidden) | rpl::start_with_next([=](bool hidden) {
|
||||
button->setVisible(!hidden);
|
||||
_hasPinnedToBottom = !hidden;
|
||||
}, button->lifetime());
|
||||
_hasPinnedToBottom = true;
|
||||
|
||||
button->setClickedCallback([=] {
|
||||
if (const auto id = _albumId.current()) {
|
||||
|
||||
@@ -72,7 +72,7 @@ private:
|
||||
void saveState(not_null<Memento*> memento);
|
||||
void restoreState(not_null<Memento*> memento);
|
||||
|
||||
void setupBottomButton(int wasBottomHeight, rpl::producer<bool> hidden);
|
||||
void setupBottomButton(int wasBottomHeight);
|
||||
void refreshBottom();
|
||||
|
||||
std::shared_ptr<ContentMemento> doCreateMemento() override;
|
||||
@@ -81,6 +81,7 @@ private:
|
||||
InnerWidget *_inner = nullptr;
|
||||
QPointer<Ui::SlideWrap<Ui::RpWidget>> _pinnedToBottom;
|
||||
rpl::variable<bool> _hasPinnedToBottom;
|
||||
rpl::variable<bool> _emptyAlbumShown;
|
||||
bool _shown = false;
|
||||
|
||||
};
|
||||
|
||||
@@ -52,10 +52,11 @@ QRect BubbleWrapInnerRect(const QRect &r) {
|
||||
not_null<Ui::RpWidget*> AddBubbleWrap(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
const QSize &size) {
|
||||
const auto bubble = container->add(object_ptr<Ui::CenterWrap<RpWidget>>(
|
||||
container,
|
||||
object_ptr<Ui::RpWidget>(container)))->entity();
|
||||
const auto bubble = container->add(
|
||||
object_ptr<Ui::RpWidget>(container),
|
||||
style::al_top);
|
||||
bubble->resize(size);
|
||||
bubble->setNaturalWidth(size.width());
|
||||
|
||||
auto cached = QImage(
|
||||
size * style::DevicePixelRatio(),
|
||||
|
||||
@@ -245,12 +245,11 @@ object_ptr<Ui::RpWidget> CreateGradientEditor(
|
||||
std::vector<QColor> colors;
|
||||
};
|
||||
const auto preview = container->add(
|
||||
object_ptr<Ui::CenterWrap<EmojiUserpic>>(
|
||||
object_ptr<EmojiUserpic>(
|
||||
container,
|
||||
object_ptr<EmojiUserpic>(
|
||||
container,
|
||||
Size(st::defaultUserpicButton.photoSize),
|
||||
false)))->entity();
|
||||
Size(st::defaultUserpicButton.photoSize),
|
||||
false),
|
||||
style::al_top);
|
||||
preview->setDuration(0);
|
||||
if (document) {
|
||||
preview->setDocument(document);
|
||||
|
||||
@@ -166,6 +166,7 @@ EmojiUserpic::EmojiUserpic(
|
||||
, _painter(size.width())
|
||||
, _duration(st::slideWrapDuration) {
|
||||
resize(size);
|
||||
setNaturalWidth(size.width());
|
||||
}
|
||||
|
||||
void EmojiUserpic::setDocument(not_null<DocumentData*> document) {
|
||||
|
||||
@@ -178,7 +178,7 @@ void ShowGradientEditor(
|
||||
},
|
||||
});
|
||||
box->setWidth(content->width());
|
||||
box->addRow(std::move(content), {});
|
||||
box->addRow(std::move(content), style::margins());
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -408,13 +408,12 @@ not_null<Ui::VerticalLayout*> CreateUserpicBuilder(
|
||||
const auto state = container->lifetime().make_state<State>();
|
||||
|
||||
const auto preview = container->add(
|
||||
object_ptr<Ui::CenterWrap<EmojiUserpic>>(
|
||||
object_ptr<EmojiUserpic>(
|
||||
container,
|
||||
object_ptr<EmojiUserpic>(
|
||||
container,
|
||||
Size(st::settingsInfoPhotoSize),
|
||||
data.isForum)),
|
||||
st::userpicBuilderEmojiPreviewPadding)->entity();
|
||||
Size(st::settingsInfoPhotoSize),
|
||||
data.isForum),
|
||||
st::userpicBuilderEmojiPreviewPadding,
|
||||
style::al_top);
|
||||
if (const auto id = data.documentId) {
|
||||
const auto document = controller->session().data().document(id);
|
||||
if (document && document->sticker()) {
|
||||
@@ -423,13 +422,12 @@ not_null<Ui::VerticalLayout*> CreateUserpicBuilder(
|
||||
}
|
||||
|
||||
container->add(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
tr::lng_userpic_builder_color_subtitle(),
|
||||
st::userpicBuilderEmojiSubtitle)),
|
||||
st::userpicBuilderEmojiSubtitlePadding);
|
||||
tr::lng_userpic_builder_color_subtitle(),
|
||||
st::userpicBuilderEmojiSubtitle),
|
||||
st::userpicBuilderEmojiSubtitlePadding,
|
||||
style::al_top);
|
||||
|
||||
const auto paletteBg = Ui::AddBubbleWrap(
|
||||
container,
|
||||
@@ -515,13 +513,12 @@ not_null<Ui::VerticalLayout*> CreateUserpicBuilder(
|
||||
}, palette->lifetime());
|
||||
|
||||
container->add(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
tr::lng_userpic_builder_emoji_subtitle(),
|
||||
st::userpicBuilderEmojiSubtitle)),
|
||||
st::userpicBuilderEmojiSubtitlePadding);
|
||||
tr::lng_userpic_builder_emoji_subtitle(),
|
||||
st::userpicBuilderEmojiSubtitle),
|
||||
st::userpicBuilderEmojiSubtitlePadding,
|
||||
style::al_top);
|
||||
|
||||
const auto selectorBg = Ui::AddBubbleWrap(
|
||||
container,
|
||||
|
||||
@@ -72,7 +72,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/dropdown_menu.h"
|
||||
#include "ui/widgets/label_with_custom_emoji.h"
|
||||
#include "ui/widgets/menu/menu_item_base.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "webview/webview_interface.h"
|
||||
@@ -384,7 +383,7 @@ void FillBotUsepic(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<PeerData*> bot,
|
||||
base::weak_ptr<Window::SessionController> weak) {
|
||||
auto aboutLabel = Ui::CreateLabelWithCustomEmoji(
|
||||
auto aboutLabel = object_ptr<Ui::FlatLabel>(
|
||||
box->verticalLayout(),
|
||||
tr::lng_allow_bot_webview_details(
|
||||
lt_emoji,
|
||||
@@ -393,7 +392,6 @@ void FillBotUsepic(
|
||||
) | rpl::map([](TextWithEntities text) {
|
||||
return Ui::Text::Link(std::move(text), u"internal:"_q);
|
||||
}),
|
||||
Core::TextContext({ .session = &bot->session() }),
|
||||
st::defaultFlatLabel);
|
||||
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
|
||||
box->verticalLayout(),
|
||||
@@ -521,7 +519,6 @@ void ConfirmEmojiStatusAccessBox(
|
||||
rpl::single(name),
|
||||
Ui::Text::RichLangValue),
|
||||
st::botEmojiStatusText),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
|
||||
box->addButton(tr::lng_bot_emoji_status_access_allow(), [=] {
|
||||
@@ -565,7 +562,6 @@ void ConfirmEmojiStatusBox(
|
||||
box,
|
||||
tr::lng_bot_emoji_status_title(),
|
||||
st::botEmojiStatusTitle),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
AddSkip(box->verticalLayout());
|
||||
|
||||
@@ -577,7 +573,6 @@ void ConfirmEmojiStatusBox(
|
||||
rpl::single(Ui::Text::Bold(bot->name())),
|
||||
Ui::Text::RichLangValue),
|
||||
st::botEmojiStatusText),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
|
||||
AddSkip(box->verticalLayout(), 2 * st::defaultVerticalListSkip);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user