mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Merge tag 'v4.9.4' into dev
# Conflicts: # Telegram/Resources/winrc/Telegram.rc # Telegram/Resources/winrc/Updater.rc # Telegram/SourceFiles/core/version.h # Telegram/lib_ui # cmake
This commit is contained in:
commit
96461b91c5
77 changed files with 1921 additions and 933 deletions
|
@ -1314,6 +1314,8 @@ PRIVATE
|
||||||
settings/settings_main.h
|
settings/settings_main.h
|
||||||
settings/settings_notifications.cpp
|
settings/settings_notifications.cpp
|
||||||
settings/settings_notifications.h
|
settings/settings_notifications.h
|
||||||
|
settings/settings_notifications_type.cpp
|
||||||
|
settings/settings_notifications_type.h
|
||||||
settings/settings_power_saving.cpp
|
settings/settings_power_saving.cpp
|
||||||
settings/settings_power_saving.h
|
settings/settings_power_saving.h
|
||||||
settings/settings_premium.cpp
|
settings/settings_premium.cpp
|
||||||
|
|
BIN
Telegram/Resources/icons/mini_forward.png
Normal file
BIN
Telegram/Resources/icons/mini_forward.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 337 B |
BIN
Telegram/Resources/icons/mini_forward@2x.png
Normal file
BIN
Telegram/Resources/icons/mini_forward@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 527 B |
BIN
Telegram/Resources/icons/mini_forward@3x.png
Normal file
BIN
Telegram/Resources/icons/mini_forward@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 772 B |
BIN
Telegram/Resources/icons/mini_reply_story.png
Normal file
BIN
Telegram/Resources/icons/mini_reply_story.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 563 B |
BIN
Telegram/Resources/icons/mini_reply_story@2x.png
Normal file
BIN
Telegram/Resources/icons/mini_reply_story@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1 KiB |
BIN
Telegram/Resources/icons/mini_reply_story@3x.png
Normal file
BIN
Telegram/Resources/icons/mini_reply_story@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
|
@ -451,6 +451,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_settings_show_from" = "Show notifications from";
|
"lng_settings_show_from" = "Show notifications from";
|
||||||
"lng_settings_notify_all" = "All accounts";
|
"lng_settings_notify_all" = "All accounts";
|
||||||
"lng_settings_notify_all_about" = "Turn this off if you want to receive notifications only from the account you are currently using.";
|
"lng_settings_notify_all_about" = "Turn this off if you want to receive notifications only from the account you are currently using.";
|
||||||
|
"lng_settings_notify_global" = "Global settings";
|
||||||
"lng_settings_notify_title" = "Notifications for chats";
|
"lng_settings_notify_title" = "Notifications for chats";
|
||||||
"lng_settings_desktop_notify" = "Desktop notifications";
|
"lng_settings_desktop_notify" = "Desktop notifications";
|
||||||
"lng_settings_native_title" = "Native notifications";
|
"lng_settings_native_title" = "Native notifications";
|
||||||
|
@ -458,8 +459,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_settings_use_native_notifications" = "Use native notifications";
|
"lng_settings_use_native_notifications" = "Use native notifications";
|
||||||
"lng_settings_notifications_position" = "Location on the screen";
|
"lng_settings_notifications_position" = "Location on the screen";
|
||||||
"lng_settings_notifications_count" = "Notifications count";
|
"lng_settings_notifications_count" = "Notifications count";
|
||||||
"lng_settings_sound_notify" = "Play sound";
|
"lng_settings_sound_allowed" = "Allow sound";
|
||||||
"lng_settings_sound_notify_off" = "Off";
|
|
||||||
"lng_settings_alert_windows" = "Flash the taskbar icon";
|
"lng_settings_alert_windows" = "Flash the taskbar icon";
|
||||||
"lng_settings_alert_mac" = "Bounce the dock icon";
|
"lng_settings_alert_mac" = "Bounce the dock icon";
|
||||||
"lng_settings_alert_linux" = "Draw attention to the window";
|
"lng_settings_alert_linux" = "Draw attention to the window";
|
||||||
|
@ -480,6 +480,36 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_notification_hide_all" = "Hide all";
|
"lng_notification_hide_all" = "Hide all";
|
||||||
"lng_notification_sample" = "This is a sample notification";
|
"lng_notification_sample" = "This is a sample notification";
|
||||||
"lng_notification_reminder" = "Reminder";
|
"lng_notification_reminder" = "Reminder";
|
||||||
|
"lng_notification_private_chats" = "Private chats";
|
||||||
|
"lng_notification_groups" = "Groups";
|
||||||
|
"lng_notification_channels" = "Channels";
|
||||||
|
"lng_notification_click_to_change" = "Click here to change";
|
||||||
|
"lng_notification_on" = "On, {exceptions}";
|
||||||
|
"lng_notification_off" = "Off, {exceptions}";
|
||||||
|
"lng_notification_exceptions#one" = "{count} exception";
|
||||||
|
"lng_notification_exceptions#other" = "{count} exceptions";
|
||||||
|
"lng_notification_exceptions_title" = "Exceptions";
|
||||||
|
"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_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_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_exceptions_view" = "View exceptions";
|
||||||
|
"lng_notification_enable" = "Enable notifications";
|
||||||
|
"lng_notification_sound" = "Sound";
|
||||||
|
"lng_notification_tone" = "Notification tone";
|
||||||
|
"lng_notification_exceptions_muted" = "Muted";
|
||||||
|
"lng_notification_exceptions_unmuted" = "Unmuted";
|
||||||
|
"lng_notification_exceptions_add" = "Add an exception";
|
||||||
|
"lng_notification_exceptions_clear" = "Delete all exceptions";
|
||||||
|
"lng_notification_exceptions_clear_sure" = "Are you sure you want to delete all exceptions?";
|
||||||
|
"lng_notification_exceptions_clear_button" = "Delete";
|
||||||
|
"lng_notification_exceptions_remove" = "Remove";
|
||||||
|
"lng_notification_context_remove" = "Remove exception";
|
||||||
|
|
||||||
"lng_reaction_text" = "{reaction} to your «{text}»";
|
"lng_reaction_text" = "{reaction} to your «{text}»";
|
||||||
"lng_reaction_notext" = "{reaction} to your message";
|
"lng_reaction_notext" = "{reaction} to your message";
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||||
ProcessorArchitecture="ARCHITECTURE"
|
ProcessorArchitecture="ARCHITECTURE"
|
||||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||||
Version="4.9.3.0" />
|
Version="4.9.4.0" />
|
||||||
<Properties>
|
<Properties>
|
||||||
<DisplayName>Telegram Desktop</DisplayName>
|
<DisplayName>Telegram Desktop</DisplayName>
|
||||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||||
|
|
|
@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 4,9,3,0
|
FILEVERSION 4,9,4,0
|
||||||
PRODUCTVERSION 4,9,3,0
|
PRODUCTVERSION 4,9,4,0
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
@ -62,10 +62,10 @@ BEGIN
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "Radolyn Labs"
|
VALUE "CompanyName", "Radolyn Labs"
|
||||||
VALUE "FileDescription", "AyuGram Desktop"
|
VALUE "FileDescription", "AyuGram Desktop"
|
||||||
VALUE "FileVersion", "4.9.3.0"
|
VALUE "FileVersion", "4.9.4.0"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2014-2023"
|
VALUE "LegalCopyright", "Copyright (C) 2014-2023"
|
||||||
VALUE "ProductName", "AyuGram Desktop"
|
VALUE "ProductName", "AyuGram Desktop"
|
||||||
VALUE "ProductVersion", "4.9.3.0"
|
VALUE "ProductVersion", "4.9.4.0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|
|
@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 4,9,3,0
|
FILEVERSION 4,9,4,0
|
||||||
PRODUCTVERSION 4,9,3,0
|
PRODUCTVERSION 4,9,4,0
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
@ -53,10 +53,10 @@ BEGIN
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "Radolyn Labs"
|
VALUE "CompanyName", "Radolyn Labs"
|
||||||
VALUE "FileDescription", "AyuGram Desktop Updater"
|
VALUE "FileDescription", "AyuGram Desktop Updater"
|
||||||
VALUE "FileVersion", "4.9.3.0"
|
VALUE "FileVersion", "4.9.4.0"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2014-2023"
|
VALUE "LegalCopyright", "Copyright (C) 2014-2023"
|
||||||
VALUE "ProductName", "AyuGram Desktop"
|
VALUE "ProductName", "AyuGram Desktop"
|
||||||
VALUE "ProductVersion", "4.9.3.0"
|
VALUE "ProductVersion", "4.9.4.0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|
|
@ -1889,17 +1889,8 @@ void ApiWrap::sendNotifySettingsUpdates() {
|
||||||
}
|
}
|
||||||
const auto &settings = session().data().notifySettings();
|
const auto &settings = session().data().notifySettings();
|
||||||
for (const auto type : base::take(_updateNotifyDefaults)) {
|
for (const auto type : base::take(_updateNotifyDefaults)) {
|
||||||
const auto input = [&] {
|
|
||||||
switch (type) {
|
|
||||||
case Data::DefaultNotify::User: return MTP_inputNotifyUsers();
|
|
||||||
case Data::DefaultNotify::Group: return MTP_inputNotifyChats();
|
|
||||||
case Data::DefaultNotify::Broadcast:
|
|
||||||
return MTP_inputNotifyBroadcasts();
|
|
||||||
}
|
|
||||||
Unexpected("Default notify type in sendNotifySettingsUpdates");
|
|
||||||
}();
|
|
||||||
request(MTPaccount_UpdateNotifySettings(
|
request(MTPaccount_UpdateNotifySettings(
|
||||||
input,
|
Data::DefaultNotifyToMTP(type),
|
||||||
settings.defaultSettings(type).serialize()
|
settings.defaultSettings(type).serialize()
|
||||||
)).afterDelay(kSmallDelayMs).send();
|
)).afterDelay(kSmallDelayMs).send();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,14 +15,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "ui/filter_icons.h"
|
|
||||||
#include "ui/text/text_utilities.h" // Ui::Text::Bold
|
#include "ui/text/text_utilities.h" // Ui::Text::Bold
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "styles/style_settings.h"
|
|
||||||
#include "styles/style_payments.h" // paymentsSectionButton
|
|
||||||
#include "styles/style_media_player.h" // mediaPlayerMenuCheck
|
#include "styles/style_media_player.h" // mediaPlayerMenuCheck
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -32,16 +29,30 @@ Data::ChatFilter ChangedFilter(
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
bool add) {
|
bool add) {
|
||||||
auto always = base::duplicate(filter.always());
|
auto always = base::duplicate(filter.always());
|
||||||
if (add) {
|
|
||||||
always.insert(history);
|
|
||||||
} else {
|
|
||||||
always.remove(history);
|
|
||||||
}
|
|
||||||
auto never = base::duplicate(filter.never());
|
auto never = base::duplicate(filter.never());
|
||||||
if (add) {
|
if (add) {
|
||||||
never.remove(history);
|
never.remove(history);
|
||||||
|
const auto result = Data::ChatFilter(
|
||||||
|
filter.id(),
|
||||||
|
filter.title(),
|
||||||
|
filter.iconEmoji(),
|
||||||
|
filter.flags(),
|
||||||
|
filter.always(),
|
||||||
|
filter.pinned(),
|
||||||
|
std::move(never));
|
||||||
|
if (result.contains(history)) {
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
never = base::duplicate(result.never());
|
||||||
|
always.insert(history);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
never.insert(history);
|
const auto alwaysIt = always.find(history);
|
||||||
|
if (alwaysIt != end(always)) {
|
||||||
|
always.erase(alwaysIt);
|
||||||
|
} else {
|
||||||
|
never.insert(history);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return Data::ChatFilter(
|
return Data::ChatFilter(
|
||||||
filter.id(),
|
filter.id(),
|
||||||
|
|
|
@ -622,9 +622,10 @@ void EditCaptionBox::setupDragArea() {
|
||||||
};
|
};
|
||||||
// Avoid both drag areas appearing at one time.
|
// Avoid both drag areas appearing at one time.
|
||||||
auto computeState = [=](const QMimeData *data) {
|
auto computeState = [=](const QMimeData *data) {
|
||||||
|
using DragState = Storage::MimeDataState;
|
||||||
const auto state = Storage::ComputeMimeDataState(data);
|
const auto state = Storage::ComputeMimeDataState(data);
|
||||||
return (state == Storage::MimeDataState::PhotoFiles)
|
return (state == DragState::PhotoFiles || state == DragState::Image)
|
||||||
? Storage::MimeDataState::Image
|
? (_asFile ? DragState::Files : DragState::Image)
|
||||||
: state;
|
: state;
|
||||||
};
|
};
|
||||||
const auto areas = DragArea::SetupDragAreaToContainer(
|
const auto areas = DragArea::SetupDragAreaToContainer(
|
||||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "boxes/peer_list_box.h"
|
#include "boxes/peer_list_box.h"
|
||||||
|
|
||||||
|
#include "history/history.h" // chatListNameSortKey.
|
||||||
#include "main/session/session_show.h"
|
#include "main/session/session_show.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
|
@ -396,6 +397,27 @@ void PeerListController::setSearchNoResultsText(const QString &text) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PeerListController::sortByName() {
|
||||||
|
auto keys = base::flat_map<PeerListRowId, QString>();
|
||||||
|
keys.reserve(delegate()->peerListFullRowsCount());
|
||||||
|
const auto key = [&](const PeerListRow &row) {
|
||||||
|
const auto id = row.id();
|
||||||
|
const auto i = keys.find(id);
|
||||||
|
if (i != end(keys)) {
|
||||||
|
return i->second;
|
||||||
|
}
|
||||||
|
const auto peer = row.peer();
|
||||||
|
const auto history = peer->owner().history(peer);
|
||||||
|
return keys.emplace(
|
||||||
|
id,
|
||||||
|
history->chatListNameSortKey()).first->second;
|
||||||
|
};
|
||||||
|
const auto predicate = [&](const PeerListRow &a, const PeerListRow &b) {
|
||||||
|
return (key(a).compare(key(b)) < 0);
|
||||||
|
};
|
||||||
|
delegate()->peerListSortRows(predicate);
|
||||||
|
}
|
||||||
|
|
||||||
base::unique_qptr<Ui::PopupMenu> PeerListController::rowContextMenu(
|
base::unique_qptr<Ui::PopupMenu> PeerListController::rowContextMenu(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
not_null<PeerListRow*> row) {
|
not_null<PeerListRow*> row) {
|
||||||
|
@ -741,8 +763,8 @@ int PeerListRow::paintNameIconGetWidth(
|
||||||
? st::dialogsVerifiedIconOver
|
? st::dialogsVerifiedIconOver
|
||||||
: st::dialogsVerifiedIcon),
|
: st::dialogsVerifiedIcon),
|
||||||
.premium = &(selected
|
.premium = &(selected
|
||||||
? st::dialogsPremiumIconOver
|
? st::dialogsPremiumIcon.over
|
||||||
: st::dialogsPremiumIcon),
|
: st::dialogsPremiumIcon.icon),
|
||||||
.scam = &(selected ? st::dialogsScamFgOver : st::dialogsScamFg),
|
.scam = &(selected ? st::dialogsScamFgOver : st::dialogsScamFg),
|
||||||
.premiumFg = &(selected
|
.premiumFg = &(selected
|
||||||
? st::dialogsVerifiedIconBgOver
|
? st::dialogsVerifiedIconBgOver
|
||||||
|
|
|
@ -560,6 +560,8 @@ protected:
|
||||||
delegate()->peerListSetSearchNoResults(std::move(noResults));
|
delegate()->peerListSetSearchNoResults(std::move(noResults));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sortByName();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PeerListDelegate *_delegate = nullptr;
|
PeerListDelegate *_delegate = nullptr;
|
||||||
std::unique_ptr<PeerListSearchController> _searchController = nullptr;
|
std::unique_ptr<PeerListSearchController> _searchController = nullptr;
|
||||||
|
|
|
@ -594,25 +594,6 @@ void ContactsBoxController::sort() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContactsBoxController::sortByName() {
|
|
||||||
auto keys = base::flat_map<PeerListRowId, QString>();
|
|
||||||
keys.reserve(delegate()->peerListFullRowsCount());
|
|
||||||
const auto key = [&](const PeerListRow &row) {
|
|
||||||
const auto id = row.id();
|
|
||||||
const auto i = keys.find(id);
|
|
||||||
if (i != end(keys)) {
|
|
||||||
return i->second;
|
|
||||||
}
|
|
||||||
const auto peer = row.peer();
|
|
||||||
const auto history = peer->owner().history(peer);
|
|
||||||
return keys.emplace(id, history->chatListNameSortKey()).first->second;
|
|
||||||
};
|
|
||||||
const auto predicate = [&](const PeerListRow &a, const PeerListRow &b) {
|
|
||||||
return (key(a).compare(key(b)) < 0);
|
|
||||||
};
|
|
||||||
delegate()->peerListSortRows(predicate);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ContactsBoxController::sortByOnline() {
|
void ContactsBoxController::sortByOnline() {
|
||||||
const auto now = base::unixtime::now();
|
const auto now = base::unixtime::now();
|
||||||
const auto key = [&](const PeerListRow &row) {
|
const auto key = [&](const PeerListRow &row) {
|
||||||
|
|
|
@ -192,7 +192,6 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void sort();
|
void sort();
|
||||||
void sortByName();
|
|
||||||
void sortByOnline();
|
void sortByOnline();
|
||||||
void rebuildRows();
|
void rebuildRows();
|
||||||
void checkForEmptyRows();
|
void checkForEmptyRows();
|
||||||
|
|
|
@ -319,16 +319,15 @@ not_null<Ui::RpWidget*> AddInnerToggle(
|
||||||
button->geometryValue(
|
button->geometryValue(
|
||||||
) | rpl::start_with_next([=](const QRect &r) {
|
) | rpl::start_with_next([=](const QRect &r) {
|
||||||
const auto w = st::rightsButtonToggleWidth;
|
const auto w = st::rightsButtonToggleWidth;
|
||||||
constexpr auto kLineWidth = int(1);
|
|
||||||
toggleButton->setGeometry(
|
toggleButton->setGeometry(
|
||||||
r.x() + r.width() - w,
|
r.x() + r.width() - w,
|
||||||
r.y(),
|
r.y(),
|
||||||
w,
|
w,
|
||||||
r.height());
|
r.height());
|
||||||
separator->setGeometry(
|
separator->setGeometry(
|
||||||
toggleButton->x() - kLineWidth,
|
toggleButton->x() - st::lineWidth,
|
||||||
r.y() + (r.height() - separatorHeight) / 2,
|
r.y() + (r.height() - separatorHeight) / 2,
|
||||||
kLineWidth,
|
st::lineWidth,
|
||||||
separatorHeight);
|
separatorHeight);
|
||||||
}, toggleButton->lifetime());
|
}, toggleButton->lifetime());
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/call_delayed.h"
|
#include "base/call_delayed.h"
|
||||||
#include "boxes/premium_limits_box.h"
|
#include "boxes/premium_limits_box.h"
|
||||||
#include "boxes/premium_preview_box.h"
|
#include "boxes/premium_preview_box.h"
|
||||||
#include "ui/boxes/confirm_box.h"
|
|
||||||
#include "ui/effects/animations.h"
|
|
||||||
#include "ui/effects/scroll_content_shadow.h"
|
#include "ui/effects/scroll_content_shadow.h"
|
||||||
#include "ui/widgets/checkbox.h"
|
#include "ui/widgets/checkbox.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
|
@ -42,9 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/chat/attach/attach_album_preview.h"
|
#include "ui/chat/attach/attach_album_preview.h"
|
||||||
#include "ui/chat/attach/attach_single_file_preview.h"
|
#include "ui/chat/attach/attach_single_file_preview.h"
|
||||||
#include "ui/chat/attach/attach_single_media_preview.h"
|
#include "ui/chat/attach/attach_single_media_preview.h"
|
||||||
#include "ui/text/format_values.h"
|
|
||||||
#include "ui/grouped_layout.h"
|
#include "ui/grouped_layout.h"
|
||||||
#include "ui/text/text_options.h"
|
|
||||||
#include "ui/toast/toast.h"
|
#include "ui/toast/toast.h"
|
||||||
#include "ui/controls/emoji_button.h"
|
#include "ui/controls/emoji_button.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
|
@ -54,16 +50,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_premium_limits.h"
|
#include "data/data_premium_limits.h"
|
||||||
#include "data/stickers/data_stickers.h"
|
#include "data/stickers/data_stickers.h"
|
||||||
#include "data/stickers/data_custom_emoji.h"
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
#include "media/clip/media_clip_reader.h"
|
|
||||||
#include "api/api_common.h"
|
#include "api/api_common.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "core/core_settings.h"
|
#include "core/core_settings.h"
|
||||||
#include "styles/style_chat.h"
|
|
||||||
#include "styles/style_layers.h"
|
#include "styles/style_layers.h"
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
#include "styles/style_chat_helpers.h"
|
#include "styles/style_chat_helpers.h"
|
||||||
#include "styles/style_info.h"
|
|
||||||
#include "styles/style_menu_icons.h"
|
#include "styles/style_menu_icons.h"
|
||||||
|
|
||||||
#include <QtCore/QMimeData>
|
#include <QtCore/QMimeData>
|
||||||
|
@ -77,10 +70,14 @@ constexpr auto kMaxMessageLength = 4096;
|
||||||
|
|
||||||
using Ui::SendFilesWay;
|
using Ui::SendFilesWay;
|
||||||
|
|
||||||
inline bool CanAddUrls(const QList<QUrl> &urls) {
|
[[nodiscard]] inline bool CanAddUrls(const QList<QUrl> &urls) {
|
||||||
return !urls.isEmpty() && ranges::all_of(urls, &QUrl::isLocalFile);
|
return !urls.isEmpty() && ranges::all_of(urls, &QUrl::isLocalFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool CanAddFiles(not_null<const QMimeData*> data) {
|
||||||
|
return data->hasImage() || CanAddUrls(Core::ReadMimeUrls(data));
|
||||||
|
}
|
||||||
|
|
||||||
void FileDialogCallback(
|
void FileDialogCallback(
|
||||||
FileDialog::OpenResult &&result,
|
FileDialog::OpenResult &&result,
|
||||||
Fn<bool(const Ui::PreparedList&)> checkResult,
|
Fn<bool(const Ui::PreparedList&)> checkResult,
|
||||||
|
@ -448,13 +445,15 @@ void SendFilesBox::setupDragArea() {
|
||||||
auto computeState = [=](const QMimeData *data) {
|
auto computeState = [=](const QMimeData *data) {
|
||||||
using DragState = Storage::MimeDataState;
|
using DragState = Storage::MimeDataState;
|
||||||
const auto state = Storage::ComputeMimeDataState(data);
|
const auto state = Storage::ComputeMimeDataState(data);
|
||||||
return (state == DragState::PhotoFiles)
|
return (state == DragState::PhotoFiles || state == DragState::Image)
|
||||||
? DragState::Image
|
? (_sendWay.current().sendImagesAsPhotos()
|
||||||
|
? DragState::Image
|
||||||
|
: DragState::Files)
|
||||||
: state;
|
: state;
|
||||||
};
|
};
|
||||||
const auto areas = DragArea::SetupDragAreaToContainer(
|
const auto areas = DragArea::SetupDragAreaToContainer(
|
||||||
this,
|
this,
|
||||||
[=](not_null<const QMimeData*> d) { return canAddFiles(d); },
|
CanAddFiles,
|
||||||
[=](bool f) { _caption->setAcceptDrops(f); },
|
[=](bool f) { _caption->setAcceptDrops(f); },
|
||||||
[=] { updateControlsGeometry(); },
|
[=] { updateControlsGeometry(); },
|
||||||
std::move(computeState));
|
std::move(computeState));
|
||||||
|
@ -1049,7 +1048,7 @@ void SendFilesBox::setupCaption() {
|
||||||
not_null<const QMimeData*> data,
|
not_null<const QMimeData*> data,
|
||||||
Ui::InputField::MimeAction action) {
|
Ui::InputField::MimeAction action) {
|
||||||
if (action == Ui::InputField::MimeAction::Check) {
|
if (action == Ui::InputField::MimeAction::Check) {
|
||||||
return canAddFiles(data);
|
return CanAddFiles(data);
|
||||||
} else if (action == Ui::InputField::MimeAction::Insert) {
|
} else if (action == Ui::InputField::MimeAction::Insert) {
|
||||||
return addFiles(data);
|
return addFiles(data);
|
||||||
}
|
}
|
||||||
|
@ -1145,10 +1144,6 @@ void SendFilesBox::captionResized() {
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SendFilesBox::canAddFiles(not_null<const QMimeData*> data) const {
|
|
||||||
return data->hasImage() || CanAddUrls(Core::ReadMimeUrls(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SendFilesBox::addFiles(not_null<const QMimeData*> data) {
|
bool SendFilesBox::addFiles(not_null<const QMimeData*> data) {
|
||||||
const auto premium = _show->session().premium();
|
const auto premium = _show->session().premium();
|
||||||
auto list = [&] {
|
auto list = [&] {
|
||||||
|
|
|
@ -213,7 +213,6 @@ private:
|
||||||
void updateControlsGeometry();
|
void updateControlsGeometry();
|
||||||
void updateCaptionPlaceholder();
|
void updateCaptionPlaceholder();
|
||||||
|
|
||||||
bool canAddFiles(not_null<const QMimeData*> data) const;
|
|
||||||
bool addFiles(not_null<const QMimeData*> data);
|
bool addFiles(not_null<const QMimeData*> data);
|
||||||
bool addFiles(Ui::PreparedList list);
|
bool addFiles(Ui::PreparedList list);
|
||||||
void addFile(Ui::PreparedFile &&file);
|
void addFile(Ui::PreparedFile &&file);
|
||||||
|
|
|
@ -18,7 +18,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/toast/toast.h"
|
#include "ui/toast/toast.h"
|
||||||
#include "ui/widgets/checkbox.h"
|
#include "ui/widgets/checkbox.h"
|
||||||
#include "ui/widgets/multi_select.h"
|
#include "ui/widgets/multi_select.h"
|
||||||
#include "ui/widgets/buttons.h"
|
|
||||||
#include "ui/widgets/scroll_area.h"
|
#include "ui/widgets/scroll_area.h"
|
||||||
#include "ui/widgets/input_fields.h"
|
#include "ui/widgets/input_fields.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
|
@ -602,7 +601,7 @@ void ShareBox::submitWhenOnline() {
|
||||||
submit(Api::DefaultSendWhenOnlineOptions());
|
submit(Api::DefaultSendWhenOnlineOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShareBox::copyLink() {
|
void ShareBox::copyLink() const {
|
||||||
if (const auto onstack = _descriptor.copyCallback) {
|
if (const auto onstack = _descriptor.copyCallback) {
|
||||||
onstack();
|
onstack();
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,7 +119,7 @@ private:
|
||||||
void submitSilent();
|
void submitSilent();
|
||||||
void submitScheduled();
|
void submitScheduled();
|
||||||
void submitWhenOnline();
|
void submitWhenOnline();
|
||||||
void copyLink();
|
void copyLink() const;
|
||||||
bool searchByUsername(bool useCache = false);
|
bool searchByUsername(bool useCache = false);
|
||||||
|
|
||||||
SendMenu::Type sendMenuType() const;
|
SendMenu::Type sendMenuType() const;
|
||||||
|
|
|
@ -53,6 +53,22 @@ constexpr auto kArchivedLimitFirstRequest = 10;
|
||||||
constexpr auto kArchivedLimitPerPage = 30;
|
constexpr auto kArchivedLimitPerPage = 30;
|
||||||
constexpr auto kHandleMegagroupSetAddressChangeTimeout = crl::time(1000);
|
constexpr auto kHandleMegagroupSetAddressChangeTimeout = crl::time(1000);
|
||||||
|
|
||||||
|
[[nodiscard]] QString FillSetTitle(
|
||||||
|
not_null<StickersSet*> set,
|
||||||
|
int maxNameWidth,
|
||||||
|
int *outTitleWidth) {
|
||||||
|
auto result = set->title;
|
||||||
|
auto titleWidth = st::contactsNameStyle.font->width(result);
|
||||||
|
if (titleWidth > maxNameWidth) {
|
||||||
|
result = st::contactsNameStyle.font->elided(result, maxNameWidth);
|
||||||
|
titleWidth = st::contactsNameStyle.font->width(result);
|
||||||
|
}
|
||||||
|
if (outTitleWidth) {
|
||||||
|
*outTitleWidth = titleWidth;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class StickersBox::CounterWidget : public Ui::RpWidget {
|
class StickersBox::CounterWidget : public Ui::RpWidget {
|
||||||
|
@ -98,22 +114,25 @@ public:
|
||||||
void updateRows(); // refresh only pack cover stickers
|
void updateRows(); // refresh only pack cover stickers
|
||||||
bool appendSet(not_null<StickersSet*> set);
|
bool appendSet(not_null<StickersSet*> set);
|
||||||
|
|
||||||
StickersSetsOrder getOrder() const;
|
StickersSetsOrder order() const;
|
||||||
StickersSetsOrder getFullOrder() const;
|
StickersSetsOrder fullOrder() const;
|
||||||
StickersSetsOrder getRemovedSets() const;
|
StickersSetsOrder removedSets() const;
|
||||||
|
|
||||||
void setFullOrder(const StickersSetsOrder &order);
|
void setFullOrder(const StickersSetsOrder &order);
|
||||||
void setRemovedSets(const StickersSetsOrder &removed);
|
void setRemovedSets(const StickersSetsOrder &removed);
|
||||||
|
|
||||||
|
void setRowRemovedBySetId(uint64 setId, bool removed);
|
||||||
|
|
||||||
void setInstallSetCallback(Fn<void(uint64 setId)> callback) {
|
void setInstallSetCallback(Fn<void(uint64 setId)> callback) {
|
||||||
_installSetCallback = std::move(callback);
|
_installSetCallback = std::move(callback);
|
||||||
}
|
}
|
||||||
|
void setRemoveSetCallback(Fn<void(uint64 setId)> callback) {
|
||||||
|
_removeSetCallback = std::move(callback);
|
||||||
|
}
|
||||||
void setLoadMoreCallback(Fn<void()> callback) {
|
void setLoadMoreCallback(Fn<void()> callback) {
|
||||||
_loadMoreCallback = std::move(callback);
|
_loadMoreCallback = std::move(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setMinHeight(int newWidth, int minHeight);
|
|
||||||
|
|
||||||
int getVisibleTop() const {
|
int getVisibleTop() const {
|
||||||
return _visibleTop;
|
return _visibleTop;
|
||||||
}
|
}
|
||||||
|
@ -151,13 +170,13 @@ private:
|
||||||
int32 pixh);
|
int32 pixh);
|
||||||
~Row();
|
~Row();
|
||||||
|
|
||||||
bool isRecentSet() const;
|
[[nodiscard]] bool isRecentSet() const;
|
||||||
bool isMasksSet() const;
|
[[nodiscard]] bool isMasksSet() const;
|
||||||
bool isEmojiSet() const;
|
[[nodiscard]] bool isEmojiSet() const;
|
||||||
bool isWebm() const;
|
[[nodiscard]] bool isWebm() const;
|
||||||
bool isInstalled() const;
|
[[nodiscard]] bool isInstalled() const;
|
||||||
bool isUnread() const;
|
[[nodiscard]] bool isUnread() const;
|
||||||
bool isArchived() const;
|
[[nodiscard]] bool isArchived() const;
|
||||||
|
|
||||||
const not_null<StickersSet*> set;
|
const not_null<StickersSet*> set;
|
||||||
DocumentData *sticker = nullptr;
|
DocumentData *sticker = nullptr;
|
||||||
|
@ -212,7 +231,11 @@ private:
|
||||||
void setPressed(SelectedRow pressed);
|
void setPressed(SelectedRow pressed);
|
||||||
void setup();
|
void setup();
|
||||||
QRect relativeButtonRect(bool removeButton, bool installedSet) const;
|
QRect relativeButtonRect(bool removeButton, bool installedSet) const;
|
||||||
void ensureRipple(const style::RippleAnimation &st, QImage mask, bool removeButton);
|
void ensureRipple(
|
||||||
|
const style::RippleAnimation &st,
|
||||||
|
QImage mask,
|
||||||
|
bool removeButton,
|
||||||
|
bool installedSet);
|
||||||
|
|
||||||
bool shiftingAnimationCallback(crl::time now);
|
bool shiftingAnimationCallback(crl::time now);
|
||||||
void paintRow(Painter &p, not_null<Row*> row, int index);
|
void paintRow(Painter &p, not_null<Row*> row, int index);
|
||||||
|
@ -237,10 +260,6 @@ private:
|
||||||
void rebuildAppendSet(not_null<StickersSet*> set);
|
void rebuildAppendSet(not_null<StickersSet*> set);
|
||||||
void fillSetCover(not_null<StickersSet*> set, DocumentData **outSticker, int *outWidth, int *outHeight) const;
|
void fillSetCover(not_null<StickersSet*> set, DocumentData **outSticker, int *outWidth, int *outHeight) const;
|
||||||
int fillSetCount(not_null<StickersSet*> set) const;
|
int fillSetCount(not_null<StickersSet*> set) const;
|
||||||
[[nodiscard]] QString fillSetTitle(
|
|
||||||
not_null<StickersSet*> set,
|
|
||||||
int maxNameWidth,
|
|
||||||
int *outTitleWidth) const;
|
|
||||||
[[nodiscard]] Data::StickersSetFlags fillSetFlags(
|
[[nodiscard]] Data::StickersSetFlags fillSetFlags(
|
||||||
not_null<StickersSet*> set) const;
|
not_null<StickersSet*> set) const;
|
||||||
void rebuildMegagroupSet();
|
void rebuildMegagroupSet();
|
||||||
|
@ -270,6 +289,7 @@ private:
|
||||||
Ui::Animations::Basic _shiftingAnimation;
|
Ui::Animations::Basic _shiftingAnimation;
|
||||||
|
|
||||||
Fn<void(uint64 setId)> _installSetCallback;
|
Fn<void(uint64 setId)> _installSetCallback;
|
||||||
|
Fn<void(uint64 setId)> _removeSetCallback;
|
||||||
Fn<void()> _loadMoreCallback;
|
Fn<void()> _loadMoreCallback;
|
||||||
|
|
||||||
int _visibleTop = 0;
|
int _visibleTop = 0;
|
||||||
|
@ -598,17 +618,43 @@ void StickersBox::prepare() {
|
||||||
_attached.widget()->hide();
|
_attached.widget()->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto installCallback = [=](uint64 setId) { installSet(setId); };
|
{
|
||||||
if (_featured.widget()) {
|
const auto installCallback = [=](uint64 setId) { installSet(setId); };
|
||||||
_featured.widget()->setInstallSetCallback(installCallback);
|
const auto markAsInstalledCallback = [=](uint64 setId) {
|
||||||
}
|
if (_installed.widget()) {
|
||||||
if (_archived.widget()) {
|
_installed.widget()->setRowRemovedBySetId(setId, false);
|
||||||
_archived.widget()->setInstallSetCallback(installCallback);
|
}
|
||||||
_archived.widget()->setLoadMoreCallback([=] { loadMoreArchived(); });
|
if (_featured.widget()) {
|
||||||
}
|
_featured.widget()->setRowRemovedBySetId(setId, false);
|
||||||
if (_attached.widget()) {
|
}
|
||||||
_attached.widget()->setInstallSetCallback(installCallback);
|
};
|
||||||
_attached.widget()->setLoadMoreCallback([=] { showAttachedStickers(); });
|
const auto markAsRemovedCallback = [=](uint64 setId) {
|
||||||
|
if (_installed.widget()) {
|
||||||
|
_installed.widget()->setRowRemovedBySetId(setId, true);
|
||||||
|
}
|
||||||
|
if (_featured.widget()) {
|
||||||
|
_featured.widget()->setRowRemovedBySetId(setId, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (const auto installed = _installed.widget()) {
|
||||||
|
installed->setInstallSetCallback(markAsInstalledCallback);
|
||||||
|
installed->setRemoveSetCallback(markAsRemovedCallback);
|
||||||
|
}
|
||||||
|
if (const auto featured = _featured.widget()) {
|
||||||
|
featured->setInstallSetCallback([=](uint64 setId) {
|
||||||
|
markAsInstalledCallback(setId);
|
||||||
|
installCallback(setId);
|
||||||
|
});
|
||||||
|
featured->setRemoveSetCallback(markAsRemovedCallback);
|
||||||
|
}
|
||||||
|
if (const auto archived = _archived.widget()) {
|
||||||
|
archived->setInstallSetCallback(installCallback);
|
||||||
|
archived->setLoadMoreCallback([=] { loadMoreArchived(); });
|
||||||
|
}
|
||||||
|
if (const auto attached = _attached.widget()) {
|
||||||
|
attached->setInstallSetCallback(installCallback);
|
||||||
|
attached->setLoadMoreCallback([=] { showAttachedStickers(); });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_megagroupSet) {
|
if (_megagroupSet) {
|
||||||
|
@ -634,7 +680,7 @@ void StickersBox::prepare() {
|
||||||
} else { // _section == Section::Featured
|
} else { // _section == Section::Featured
|
||||||
_tab = &_featured;
|
_tab = &_featured;
|
||||||
}
|
}
|
||||||
setInnerWidget(_tab->takeWidget(), getTopSkip());
|
setInnerWidget(_tab->takeWidget(), topSkip());
|
||||||
setDimensions(st::boxWideWidth, st::boxMaxListHeight);
|
setDimensions(st::boxWideWidth, st::boxMaxListHeight);
|
||||||
|
|
||||||
session().data().stickers().updated(_isEmoji
|
session().data().stickers().updated(_isEmoji
|
||||||
|
@ -757,7 +803,7 @@ void StickersBox::paintEvent(QPaintEvent *e) {
|
||||||
Painter p(this);
|
Painter p(this);
|
||||||
|
|
||||||
if (_slideAnimation) {
|
if (_slideAnimation) {
|
||||||
_slideAnimation->paintFrame(p, 0, getTopSkip(), width());
|
_slideAnimation->paintFrame(p, 0, topSkip(), width());
|
||||||
if (!_slideAnimation->animating()) {
|
if (!_slideAnimation->animating()) {
|
||||||
_slideAnimation.reset();
|
_slideAnimation.reset();
|
||||||
setInnerVisible(true);
|
setInnerVisible(true);
|
||||||
|
@ -774,7 +820,7 @@ void StickersBox::updateTabsGeometry() {
|
||||||
_tabs->resizeToWidth(_tabIndices.size() * width() / maxTabs);
|
_tabs->resizeToWidth(_tabIndices.size() * width() / maxTabs);
|
||||||
_unreadBadge->setVisible(_tabIndices.contains(Section::Featured));
|
_unreadBadge->setVisible(_tabIndices.contains(Section::Featured));
|
||||||
|
|
||||||
setInnerTopSkip(getTopSkip());
|
setInnerTopSkip(topSkip());
|
||||||
|
|
||||||
auto featuredLeft = width() / maxTabs;
|
auto featuredLeft = width() / maxTabs;
|
||||||
auto featuredRight = 2 * width() / maxTabs;
|
auto featuredRight = 2 * width() / maxTabs;
|
||||||
|
@ -790,7 +836,7 @@ void StickersBox::updateTabsGeometry() {
|
||||||
_tabs->moveToLeft(0, 0);
|
_tabs->moveToLeft(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int StickersBox::getTopSkip() const {
|
int StickersBox::topSkip() const {
|
||||||
return _tabs ? (_tabs->height() - st::lineWidth) : 0;
|
return _tabs ? (_tabs->height() - st::lineWidth) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -819,8 +865,8 @@ void StickersBox::switchTab() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_tab == &_installed) {
|
if (_tab == &_installed) {
|
||||||
_localOrder = _tab->widget()->getFullOrder();
|
_localOrder = _tab->widget()->fullOrder();
|
||||||
_localRemoved = _tab->widget()->getRemovedSets();
|
_localRemoved = _tab->widget()->removedSets();
|
||||||
}
|
}
|
||||||
auto wasCache = grabContentCache();
|
auto wasCache = grabContentCache();
|
||||||
auto wasIndex = _tab->index();
|
auto wasIndex = _tab->index();
|
||||||
|
@ -831,12 +877,12 @@ void StickersBox::switchTab() {
|
||||||
_tab->returnWidget(std::move(widget));
|
_tab->returnWidget(std::move(widget));
|
||||||
_tab = newTab;
|
_tab = newTab;
|
||||||
_section = newSection;
|
_section = newSection;
|
||||||
setInnerWidget(_tab->takeWidget(), getTopSkip());
|
setInnerWidget(_tab->takeWidget(), topSkip());
|
||||||
_tabs->raise();
|
_tabs->raise();
|
||||||
_unreadBadge->raise();
|
_unreadBadge->raise();
|
||||||
_tab->widget()->show();
|
_tab->widget()->show();
|
||||||
rebuildList();
|
rebuildList();
|
||||||
scrollToY(_tab->getScrollTop());
|
scrollToY(_tab->scrollTop());
|
||||||
setInnerVisible(true);
|
setInnerVisible(true);
|
||||||
auto nowCache = grabContentCache();
|
auto nowCache = grabContentCache();
|
||||||
auto nowIndex = _tab->index();
|
auto nowIndex = _tab->index();
|
||||||
|
@ -901,7 +947,7 @@ void StickersBox::installSet(uint64 setId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void StickersBox::installDone(
|
void StickersBox::installDone(
|
||||||
const MTPmessages_StickerSetInstallResult &result) {
|
const MTPmessages_StickerSetInstallResult &result) const {
|
||||||
if (result.type() == mtpc_messages_stickerSetInstallResultArchive) {
|
if (result.type() == mtpc_messages_stickerSetInstallResultArchive) {
|
||||||
session().data().stickers().applyArchivedResult(
|
session().data().stickers().applyArchivedResult(
|
||||||
result.c_messages_stickerSetInstallResultArchive());
|
result.c_messages_stickerSetInstallResultArchive());
|
||||||
|
@ -998,12 +1044,12 @@ void StickersBox::rebuildList(Tab *tab) {
|
||||||
tab = _tab;
|
tab = _tab;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((tab == &_installed) || (tab == &_masks)) {
|
if ((tab == &_installed) || (tab == &_masks) || (_tab == &_featured)) {
|
||||||
_localOrder = tab->widget()->getFullOrder();
|
_localOrder = tab->widget()->fullOrder();
|
||||||
_localRemoved = tab->widget()->getRemovedSets();
|
_localRemoved = tab->widget()->removedSets();
|
||||||
}
|
}
|
||||||
tab->widget()->rebuild(_isMasks);
|
tab->widget()->rebuild(_isMasks);
|
||||||
if ((tab == &_installed) || (tab == &_masks)) {
|
if ((tab == &_installed) || (tab == &_masks) || (_tab == &_featured)) {
|
||||||
tab->widget()->setFullOrder(_localOrder);
|
tab->widget()->setFullOrder(_localOrder);
|
||||||
}
|
}
|
||||||
tab->widget()->setRemovedSets(_localRemoved);
|
tab->widget()->setRemovedSets(_localRemoved);
|
||||||
|
@ -1030,14 +1076,14 @@ void StickersBox::saveChanges() {
|
||||||
}
|
}
|
||||||
if (installed) {
|
if (installed) {
|
||||||
session().api().saveStickerSets(
|
session().api().saveStickerSets(
|
||||||
installed->getOrder(),
|
installed->order(),
|
||||||
installed->getRemovedSets(),
|
installed->removedSets(),
|
||||||
Data::StickersType::Stickers);
|
Data::StickersType::Stickers);
|
||||||
}
|
}
|
||||||
if (masks) {
|
if (masks) {
|
||||||
session().api().saveStickerSets(
|
session().api().saveStickerSets(
|
||||||
masks->getOrder(),
|
masks->order(),
|
||||||
masks->getRemovedSets(),
|
masks->removedSets(),
|
||||||
Data::StickersType::Masks);
|
Data::StickersType::Masks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1056,7 +1102,7 @@ const Data::StickersSetsOrder &StickersBox::archivedSetsOrder() const {
|
||||||
: session().data().stickers().archivedMaskSetsOrder();
|
: session().data().stickers().archivedMaskSetsOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
Data::StickersSetsOrder &StickersBox::archivedSetsOrderRef() {
|
Data::StickersSetsOrder &StickersBox::archivedSetsOrderRef() const {
|
||||||
return !_isMasks
|
return !_isMasks
|
||||||
? session().data().stickers().archivedSetsOrderRef()
|
? session().data().stickers().archivedSetsOrderRef()
|
||||||
: session().data().stickers().archivedMaskSetsOrderRef();
|
: session().data().stickers().archivedMaskSetsOrderRef();
|
||||||
|
@ -1594,6 +1640,12 @@ void StickersBox::Inner::paintFakeButton(Painter &p, not_null<Row*> row, int ind
|
||||||
const auto textWidth = _installedWidth;
|
const auto textWidth = _installedWidth;
|
||||||
const auto &text = _installedText;
|
const auto &text = _installedText;
|
||||||
_inactiveButtonBg.paint(p, myrtlrect(rect));
|
_inactiveButtonBg.paint(p, myrtlrect(rect));
|
||||||
|
if (row->ripple) {
|
||||||
|
row->ripple->paint(p, rect.x(), rect.y(), width());
|
||||||
|
if (row->ripple->empty()) {
|
||||||
|
row->ripple.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
p.setFont(st.font);
|
p.setFont(st.font);
|
||||||
p.setPen(st.textFg);
|
p.setPen(st.textFg);
|
||||||
p.drawTextLeft(rect.x() - (st.width / 2), rect.y() + st.textTop, width(), text, textWidth);
|
p.drawTextLeft(rect.x() - (st.width / 2), rect.y() + st.textTop, width(), text, textWidth);
|
||||||
|
@ -1673,16 +1725,27 @@ void StickersBox::Inner::setActionDown(int newActionDown) {
|
||||||
if (row->removed) {
|
if (row->removed) {
|
||||||
auto rippleSize = QSize(_undoWidth - st::stickersUndoRemove.width, st::stickersUndoRemove.height);
|
auto rippleSize = QSize(_undoWidth - st::stickersUndoRemove.width, st::stickersUndoRemove.height);
|
||||||
auto rippleMask = Ui::RippleAnimation::RoundRectMask(rippleSize, st::roundRadiusLarge);
|
auto rippleMask = Ui::RippleAnimation::RoundRectMask(rippleSize, st::roundRadiusLarge);
|
||||||
ensureRipple(st::stickersUndoRemove.ripple, std::move(rippleMask), removeButton);
|
ensureRipple(st::stickersUndoRemove.ripple, std::move(rippleMask), removeButton, false);
|
||||||
} else {
|
} else {
|
||||||
auto rippleSize = st::stickersRemove.rippleAreaSize;
|
auto rippleSize = st::stickersRemove.rippleAreaSize;
|
||||||
auto rippleMask = Ui::RippleAnimation::EllipseMask(QSize(rippleSize, rippleSize));
|
auto rippleMask = Ui::RippleAnimation::EllipseMask(QSize(rippleSize, rippleSize));
|
||||||
ensureRipple(st::stickersRemove.ripple, std::move(rippleMask), removeButton);
|
ensureRipple(st::stickersRemove.ripple, std::move(rippleMask), removeButton, false);
|
||||||
}
|
}
|
||||||
} else if (!row->isInstalled() || row->isArchived() || row->removed) {
|
} else {
|
||||||
auto rippleSize = QSize(_addWidth - st::stickersTrendingAdd.width, st::stickersTrendingAdd.height);
|
const auto installedSet = row->isInstalled()
|
||||||
auto rippleMask = Ui::RippleAnimation::RoundRectMask(rippleSize, st::roundRadiusLarge);
|
&& !row->isArchived()
|
||||||
ensureRipple(st::stickersTrendingAdd.ripple, std::move(rippleMask), removeButton);
|
&& !row->removed;
|
||||||
|
const auto &st = installedSet
|
||||||
|
? st::stickersTrendingInstalled
|
||||||
|
: st::stickersTrendingAdd;
|
||||||
|
auto rippleMask = Ui::RippleAnimation::RoundRectMask(
|
||||||
|
QSize(_addWidth - st.width, st.height),
|
||||||
|
st::roundRadiusLarge);
|
||||||
|
ensureRipple(
|
||||||
|
st.ripple,
|
||||||
|
std::move(rippleMask),
|
||||||
|
removeButton,
|
||||||
|
installedSet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (row->ripple) {
|
if (row->ripple) {
|
||||||
|
@ -1747,9 +1810,14 @@ void StickersBox::Inner::setPressed(SelectedRow pressed) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StickersBox::Inner::ensureRipple(const style::RippleAnimation &st, QImage mask, bool removeButton) {
|
void StickersBox::Inner::ensureRipple(
|
||||||
_rows[_actionDown]->ripple = std::make_unique<Ui::RippleAnimation>(st, std::move(mask), [this, index = _actionDown, removeButton] {
|
const style::RippleAnimation &st,
|
||||||
update(myrtlrect(relativeButtonRect(removeButton, false).translated(0, _itemsTop + index * _rowHeight)));
|
QImage mask,
|
||||||
|
bool removeButton,
|
||||||
|
bool installedSet) {
|
||||||
|
const auto dy = _itemsTop + _actionDown * _rowHeight;
|
||||||
|
_rows[_actionDown]->ripple = std::make_unique<Ui::RippleAnimation>(st, std::move(mask), [=] {
|
||||||
|
update(myrtlrect(relativeButtonRect(removeButton, installedSet).translated(0, dy)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1812,7 +1880,7 @@ void StickersBox::Inner::updateSelected() {
|
||||||
selected = selectedIndex;
|
selected = selectedIndex;
|
||||||
local.setY(local.y() - _itemsTop - selectedIndex * _rowHeight);
|
local.setY(local.y() - _itemsTop - selectedIndex * _rowHeight);
|
||||||
const auto row = _rows[selectedIndex].get();
|
const auto row = _rows[selectedIndex].get();
|
||||||
if (!_megagroupSet && (_isInstalledTab || !row->isInstalled() || row->isArchived() || row->removed)) {
|
if (!_megagroupSet && (_isInstalledTab || (_section == Section::Featured) || !row->isInstalled() || row->isArchived() || row->removed)) {
|
||||||
auto removeButton = (_isInstalledTab && !row->removed);
|
auto removeButton = (_isInstalledTab && !row->removed);
|
||||||
auto rect = myrtlrect(relativeButtonRect(removeButton, false));
|
auto rect = myrtlrect(relativeButtonRect(removeButton, false));
|
||||||
actionSel = rect.contains(local) ? selectedIndex : -1;
|
actionSel = rect.contains(local) ? selectedIndex : -1;
|
||||||
|
@ -1857,7 +1925,7 @@ float64 StickersBox::Inner::aboveShadowOpacity() const {
|
||||||
|
|
||||||
auto dx = 0;
|
auto dx = 0;
|
||||||
auto dy = qAbs(_above * _rowHeight + qRound(_rows[_above]->yadd.current()) - _started * _rowHeight);
|
auto dy = qAbs(_above * _rowHeight + qRound(_rows[_above]->yadd.current()) - _started * _rowHeight);
|
||||||
return qMin((dx + dy) * 2. / _rowHeight, 1.);
|
return qMin((dx + dy) * 2. / _rowHeight, 1.);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
|
@ -1868,10 +1936,11 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
_mouse = e->globalPos();
|
_mouse = e->globalPos();
|
||||||
updateSelected();
|
updateSelected();
|
||||||
if (_actionDown == _actionSel && _actionSel >= 0) {
|
if (_actionDown == _actionSel && _actionSel >= 0) {
|
||||||
if (_isInstalledTab) {
|
const auto callback = _rows[_actionDown]->removed
|
||||||
setRowRemoved(_actionDown, !_rows[_actionDown]->removed);
|
? _installSetCallback
|
||||||
} else if (_installSetCallback) {
|
: _removeSetCallback;
|
||||||
_installSetCallback(_rows[_actionDown]->set->id);
|
if (callback) {
|
||||||
|
callback(_rows[_actionDown]->set->id);
|
||||||
}
|
}
|
||||||
} else if (_dragging >= 0) {
|
} else if (_dragging >= 0) {
|
||||||
_rows[_dragging]->yadd.start(0.);
|
_rows[_dragging]->yadd.start(0.);
|
||||||
|
@ -1921,6 +1990,13 @@ void StickersBox::Inner::saveGroupSet() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StickersBox::Inner::setRowRemovedBySetId(uint64 setId, bool removed) {
|
||||||
|
const auto index = getRowIndex(setId);
|
||||||
|
if (index >= 0) {
|
||||||
|
setRowRemoved(index, removed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void StickersBox::Inner::setRowRemoved(int index, bool removed) {
|
void StickersBox::Inner::setRowRemoved(int index, bool removed) {
|
||||||
auto &row = _rows[index];
|
auto &row = _rows[index];
|
||||||
if (row->removed != removed) {
|
if (row->removed != removed) {
|
||||||
|
@ -2114,7 +2190,7 @@ void StickersBox::Inner::rebuildMegagroupSet() {
|
||||||
auto removed = false;
|
auto removed = false;
|
||||||
auto maxNameWidth = countMaxNameWidth(!_isInstalledTab);
|
auto maxNameWidth = countMaxNameWidth(!_isInstalledTab);
|
||||||
auto titleWidth = 0;
|
auto titleWidth = 0;
|
||||||
auto title = fillSetTitle(set, maxNameWidth, &titleWidth);
|
auto title = FillSetTitle(set, maxNameWidth, &titleWidth);
|
||||||
if (!_megagroupSelectedSet
|
if (!_megagroupSelectedSet
|
||||||
|| _megagroupSelectedSet->set->id != set->id) {
|
|| _megagroupSelectedSet->set->id != set->id) {
|
||||||
_megagroupSetField->setText(set->shortName);
|
_megagroupSetField->setText(set->shortName);
|
||||||
|
@ -2217,11 +2293,6 @@ void StickersBox::Inner::setMegagroupSelectedSet(
|
||||||
updateSelected();
|
updateSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
void StickersBox::Inner::setMinHeight(int newWidth, int minHeight) {
|
|
||||||
_minHeight = minHeight;
|
|
||||||
updateSize(newWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
void StickersBox::Inner::updateSize(int newWidth) {
|
void StickersBox::Inner::updateSize(int newWidth) {
|
||||||
auto naturalHeight = _itemsTop + int(_rows.size()) * _rowHeight + st::membersMarginBottom;
|
auto naturalHeight = _itemsTop + int(_rows.size()) * _rowHeight + st::membersMarginBottom;
|
||||||
resize(newWidth ? newWidth : width(), qMax(_minHeight, naturalHeight));
|
resize(newWidth ? newWidth : width(), qMax(_minHeight, naturalHeight));
|
||||||
|
@ -2269,7 +2340,7 @@ void StickersBox::Inner::updateRows() {
|
||||||
&& row->isInstalled()
|
&& row->isInstalled()
|
||||||
&& !row->isArchived()
|
&& !row->isArchived()
|
||||||
&& !row->removed);
|
&& !row->removed);
|
||||||
row->title = fillSetTitle(
|
row->title = FillSetTitle(
|
||||||
set,
|
set,
|
||||||
installedSet ? maxNameWidthInstalled : maxNameWidth,
|
installedSet ? maxNameWidthInstalled : maxNameWidth,
|
||||||
&row->titleWidth);
|
&row->titleWidth);
|
||||||
|
@ -2331,7 +2402,7 @@ void StickersBox::Inner::rebuildAppendSet(not_null<StickersSet*> set) {
|
||||||
&& !(flagsOverride & SetFlag::Archived)
|
&& !(flagsOverride & SetFlag::Archived)
|
||||||
&& !removed);
|
&& !removed);
|
||||||
int titleWidth = 0;
|
int titleWidth = 0;
|
||||||
QString title = fillSetTitle(set, maxNameWidth, &titleWidth);
|
QString title = FillSetTitle(set, maxNameWidth, &titleWidth);
|
||||||
int count = fillSetCount(set);
|
int count = fillSetCount(set);
|
||||||
|
|
||||||
const auto existing = [&]{
|
const auto existing = [&]{
|
||||||
|
@ -2458,22 +2529,6 @@ int StickersBox::Inner::fillSetCount(not_null<StickersSet*> set) const {
|
||||||
return result + added;
|
return result + added;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString StickersBox::Inner::fillSetTitle(
|
|
||||||
not_null<StickersSet*> set,
|
|
||||||
int maxNameWidth,
|
|
||||||
int *outTitleWidth) const {
|
|
||||||
auto result = set->title;
|
|
||||||
int titleWidth = st::contactsNameStyle.font->width(result);
|
|
||||||
if (titleWidth > maxNameWidth) {
|
|
||||||
result = st::contactsNameStyle.font->elided(result, maxNameWidth);
|
|
||||||
titleWidth = st::contactsNameStyle.font->width(result);
|
|
||||||
}
|
|
||||||
if (outTitleWidth) {
|
|
||||||
*outTitleWidth = titleWidth;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Data::StickersSetFlags StickersBox::Inner::fillSetFlags(
|
Data::StickersSetFlags StickersBox::Inner::fillSetFlags(
|
||||||
not_null<StickersSet*> set) const {
|
not_null<StickersSet*> set) const {
|
||||||
const auto result = set->flags;
|
const auto result = set->flags;
|
||||||
|
@ -2494,19 +2549,19 @@ StickersSetsOrder StickersBox::Inner::collectSets(Check check) const {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
StickersSetsOrder StickersBox::Inner::getOrder() const {
|
StickersSetsOrder StickersBox::Inner::order() const {
|
||||||
return collectSets([](Row *row) {
|
return collectSets([](Row *row) {
|
||||||
return !row->isArchived() && !row->removed && !row->isRecentSet();
|
return !row->isArchived() && !row->removed && !row->isRecentSet();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
StickersSetsOrder StickersBox::Inner::getFullOrder() const {
|
StickersSetsOrder StickersBox::Inner::fullOrder() const {
|
||||||
return collectSets([](Row *row) {
|
return collectSets([](Row *row) {
|
||||||
return !row->isRecentSet();
|
return !row->isRecentSet();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
StickersSetsOrder StickersBox::Inner::getRemovedSets() const {
|
StickersSetsOrder StickersBox::Inner::removedSets() const {
|
||||||
return collectSets([](Row *row) {
|
return collectSets([](Row *row) {
|
||||||
return row->removed;
|
return row->removed;
|
||||||
});
|
});
|
||||||
|
|
|
@ -104,7 +104,7 @@ private:
|
||||||
[[nodiscard]] int index() const;
|
[[nodiscard]] int index() const;
|
||||||
|
|
||||||
void saveScrollTop();
|
void saveScrollTop();
|
||||||
int getScrollTop() const {
|
int scrollTop() const {
|
||||||
return _scrollTop;
|
return _scrollTop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,12 +122,12 @@ private:
|
||||||
void updateTabsGeometry();
|
void updateTabsGeometry();
|
||||||
void switchTab();
|
void switchTab();
|
||||||
void installSet(uint64 setId);
|
void installSet(uint64 setId);
|
||||||
int getTopSkip() const;
|
int topSkip() const;
|
||||||
void saveChanges();
|
void saveChanges();
|
||||||
|
|
||||||
QPixmap grabContentCache();
|
QPixmap grabContentCache();
|
||||||
|
|
||||||
void installDone(const MTPmessages_StickerSetInstallResult &result);
|
void installDone(const MTPmessages_StickerSetInstallResult &result) const;
|
||||||
void installFail(const MTP::Error &error, uint64 setId);
|
void installFail(const MTP::Error &error, uint64 setId);
|
||||||
|
|
||||||
void preloadArchivedSets();
|
void preloadArchivedSets();
|
||||||
|
@ -139,7 +139,7 @@ private:
|
||||||
void showAttachedStickers();
|
void showAttachedStickers();
|
||||||
|
|
||||||
const Data::StickersSetsOrder &archivedSetsOrder() const;
|
const Data::StickersSetsOrder &archivedSetsOrder() const;
|
||||||
Data::StickersSetsOrder &archivedSetsOrderRef();
|
Data::StickersSetsOrder &archivedSetsOrderRef() const;
|
||||||
|
|
||||||
std::array<Inner*, 5> widgets() const;
|
std::array<Inner*, 5> widgets() const;
|
||||||
|
|
||||||
|
|
|
@ -294,8 +294,11 @@ stickersTrendingAdd: RoundButton(defaultActiveButton) {
|
||||||
stickersTrendingInstalled: RoundButton(stickersTrendingAdd) {
|
stickersTrendingInstalled: RoundButton(stickersTrendingAdd) {
|
||||||
textFg: activeButtonBg;
|
textFg: activeButtonBg;
|
||||||
textFgOver: activeButtonBgOver;
|
textFgOver: activeButtonBgOver;
|
||||||
textBg: activeButtonSecondaryFg;
|
textBg: lightButtonBgOver;
|
||||||
textBgOver: activeButtonSecondaryFgOver;
|
textBgOver: lightButtonBgOver;
|
||||||
|
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||||
|
color: activeButtonSecondaryFg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
stickersRemove: IconButton(defaultIconButton) {
|
stickersRemove: IconButton(defaultIconButton) {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
|
|
|
@ -45,12 +45,9 @@ inline QString IconName() {
|
||||||
|
|
||||||
inline bool CanReadDirectory(const QString &path) {
|
inline bool CanReadDirectory(const QString &path) {
|
||||||
#ifndef Q_OS_MAC // directory_iterator since 10.15
|
#ifndef Q_OS_MAC // directory_iterator since 10.15
|
||||||
try {
|
std::error_code error;
|
||||||
std::filesystem::directory_iterator(path.toStdString());
|
std::filesystem::directory_iterator(path.toStdString(), error);
|
||||||
return true;
|
return !error;
|
||||||
} catch (...) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
Unexpected("Not implemented.");
|
Unexpected("Not implemented.");
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D666}"_cs;
|
||||||
constexpr auto AppNameOld = "AyuGram for Windows"_cs;
|
constexpr auto AppNameOld = "AyuGram for Windows"_cs;
|
||||||
constexpr auto AppName = "AyuGram Desktop"_cs;
|
constexpr auto AppName = "AyuGram Desktop"_cs;
|
||||||
constexpr auto AppFile = "AyuGram"_cs;
|
constexpr auto AppFile = "AyuGram"_cs;
|
||||||
constexpr auto AppVersion = 4009003;
|
constexpr auto AppVersion = 4009004;
|
||||||
constexpr auto AppVersionStr = "4.9.3";
|
constexpr auto AppVersionStr = "4.9.4";
|
||||||
constexpr auto AppBetaVersion = false;
|
constexpr auto AppBetaVersion = false;
|
||||||
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
||||||
|
|
|
@ -42,11 +42,39 @@ constexpr auto kMaxNotifyCheckDelay = 24 * 3600 * crl::time(1000);
|
||||||
return (result > 0);
|
return (result > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool SkipAddException(not_null<PeerData*> peer) {
|
||||||
|
if (const auto user = peer->asUser()) {
|
||||||
|
return user->isInaccessible();
|
||||||
|
} else if (const auto chat = peer->asChat()) {
|
||||||
|
return chat->isDeactivated() || chat->isForbidden();
|
||||||
|
} else if (const auto channel = peer->asChannel()) {
|
||||||
|
return channel->isForbidden();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
DefaultNotify DefaultNotifyType(not_null<const PeerData*> peer) {
|
||||||
|
return peer->isUser()
|
||||||
|
? DefaultNotify::User
|
||||||
|
: (peer->isChat() || peer->isMegagroup())
|
||||||
|
? DefaultNotify::Group
|
||||||
|
: DefaultNotify::Broadcast;
|
||||||
|
}
|
||||||
|
|
||||||
|
MTPInputNotifyPeer DefaultNotifyToMTP(DefaultNotify type) {
|
||||||
|
switch (type) {
|
||||||
|
case DefaultNotify::User: return MTP_inputNotifyUsers();
|
||||||
|
case DefaultNotify::Group: return MTP_inputNotifyChats();
|
||||||
|
case DefaultNotify::Broadcast: return MTP_inputNotifyBroadcasts();
|
||||||
|
}
|
||||||
|
Unexpected("Default notify type in sendNotifySettingsUpdates");
|
||||||
|
}
|
||||||
|
|
||||||
NotifySettings::NotifySettings(not_null<Session*> owner)
|
NotifySettings::NotifySettings(not_null<Session*> owner)
|
||||||
: _owner(owner)
|
: _owner(owner)
|
||||||
, _unmuteByFinishedTimer([=] { unmuteByFinished(); }) {
|
, _unmuteByFinishedTimer([=] { unmuteByFinished(); }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotifySettings::request(not_null<PeerData*> peer) {
|
void NotifySettings::request(not_null<PeerData*> peer) {
|
||||||
|
@ -63,7 +91,7 @@ void NotifySettings::request(not_null<PeerData*> peer) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotifySettings::request(not_null<Data::Thread*> thread) {
|
void NotifySettings::request(not_null<Thread*> thread) {
|
||||||
if (const auto topic = thread->asTopic()) {
|
if (const auto topic = thread->asTopic()) {
|
||||||
if (topic->notify().settingsUnknown()) {
|
if (topic->notify().settingsUnknown()) {
|
||||||
topic->session().api().requestNotifySettings(
|
topic->session().api().requestNotifySettings(
|
||||||
|
@ -145,6 +173,7 @@ void NotifySettings::apply(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
const MTPPeerNotifySettings &settings) {
|
const MTPPeerNotifySettings &settings) {
|
||||||
if (peer->notify().change(settings)) {
|
if (peer->notify().change(settings)) {
|
||||||
|
updateException(peer);
|
||||||
updateLocal(peer);
|
updateLocal(peer);
|
||||||
Core::App().notifications().checkDelayed();
|
Core::App().notifications().checkDelayed();
|
||||||
}
|
}
|
||||||
|
@ -162,7 +191,7 @@ void NotifySettings::apply(
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotifySettings::apply(
|
void NotifySettings::apply(
|
||||||
not_null<Data::ForumTopic*> topic,
|
not_null<ForumTopic*> topic,
|
||||||
const MTPPeerNotifySettings &settings) {
|
const MTPPeerNotifySettings &settings) {
|
||||||
if (topic->notify().change(settings)) {
|
if (topic->notify().change(settings)) {
|
||||||
updateLocal(topic);
|
updateLocal(topic);
|
||||||
|
@ -171,8 +200,8 @@ void NotifySettings::apply(
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotifySettings::update(
|
void NotifySettings::update(
|
||||||
not_null<Data::Thread*> thread,
|
not_null<Thread*> thread,
|
||||||
Data::MuteValue muteForSeconds,
|
MuteValue muteForSeconds,
|
||||||
std::optional<bool> silentPosts,
|
std::optional<bool> silentPosts,
|
||||||
std::optional<NotifySound> sound,
|
std::optional<NotifySound> sound,
|
||||||
std::optional<bool> storiesMuted) {
|
std::optional<bool> storiesMuted) {
|
||||||
|
@ -181,34 +210,29 @@ void NotifySettings::update(
|
||||||
silentPosts,
|
silentPosts,
|
||||||
sound,
|
sound,
|
||||||
storiesMuted)) {
|
storiesMuted)) {
|
||||||
|
if (const auto history = thread->asHistory()) {
|
||||||
|
updateException(history->peer);
|
||||||
|
}
|
||||||
updateLocal(thread);
|
updateLocal(thread);
|
||||||
thread->session().api().updateNotifySettingsDelayed(thread);
|
thread->session().api().updateNotifySettingsDelayed(thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotifySettings::resetToDefault(not_null<Data::Thread*> thread) {
|
void NotifySettings::resetToDefault(not_null<Thread*> thread) {
|
||||||
const auto empty = MTP_peerNotifySettings(
|
// Duplicated in clearExceptions(type) and resetToDefault(peer).
|
||||||
MTP_flags(0),
|
if (thread->notify().resetToDefault()) {
|
||||||
MTPBool(),
|
if (const auto history = thread->asHistory()) {
|
||||||
MTPBool(),
|
updateException(history->peer);
|
||||||
MTPint(),
|
}
|
||||||
MTPNotificationSound(),
|
|
||||||
MTPNotificationSound(),
|
|
||||||
MTPNotificationSound(),
|
|
||||||
MTPBool(),
|
|
||||||
MTPBool(),
|
|
||||||
MTPNotificationSound(),
|
|
||||||
MTPNotificationSound(),
|
|
||||||
MTPNotificationSound());
|
|
||||||
if (thread->notify().change(empty)) {
|
|
||||||
updateLocal(thread);
|
updateLocal(thread);
|
||||||
thread->session().api().updateNotifySettingsDelayed(thread);
|
thread->session().api().updateNotifySettingsDelayed(thread);
|
||||||
|
Core::App().notifications().checkDelayed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotifySettings::update(
|
void NotifySettings::update(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
Data::MuteValue muteForSeconds,
|
MuteValue muteForSeconds,
|
||||||
std::optional<bool> silentPosts,
|
std::optional<bool> silentPosts,
|
||||||
std::optional<NotifySound> sound,
|
std::optional<NotifySound> sound,
|
||||||
std::optional<bool> storiesMuted) {
|
std::optional<bool> storiesMuted) {
|
||||||
|
@ -217,33 +241,24 @@ void NotifySettings::update(
|
||||||
silentPosts,
|
silentPosts,
|
||||||
sound,
|
sound,
|
||||||
storiesMuted)) {
|
storiesMuted)) {
|
||||||
|
updateException(peer);
|
||||||
updateLocal(peer);
|
updateLocal(peer);
|
||||||
peer->session().api().updateNotifySettingsDelayed(peer);
|
peer->session().api().updateNotifySettingsDelayed(peer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotifySettings::resetToDefault(not_null<PeerData*> peer) {
|
void NotifySettings::resetToDefault(not_null<PeerData*> peer) {
|
||||||
const auto empty = MTP_peerNotifySettings(
|
// Duplicated in clearExceptions(type) and resetToDefault(thread).
|
||||||
MTP_flags(0),
|
if (peer->notify().resetToDefault()) {
|
||||||
MTPBool(),
|
updateException(peer);
|
||||||
MTPBool(),
|
|
||||||
MTPint(),
|
|
||||||
MTPNotificationSound(),
|
|
||||||
MTPNotificationSound(),
|
|
||||||
MTPNotificationSound(),
|
|
||||||
MTPBool(),
|
|
||||||
MTPBool(),
|
|
||||||
MTPNotificationSound(),
|
|
||||||
MTPNotificationSound(),
|
|
||||||
MTPNotificationSound());
|
|
||||||
if (peer->notify().change(empty)) {
|
|
||||||
updateLocal(peer);
|
updateLocal(peer);
|
||||||
peer->session().api().updateNotifySettingsDelayed(peer);
|
peer->session().api().updateNotifySettingsDelayed(peer);
|
||||||
|
Core::App().notifications().checkDelayed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotifySettings::forumParentMuteUpdated(not_null<Data::Forum*> forum) {
|
void NotifySettings::forumParentMuteUpdated(not_null<Forum*> forum) {
|
||||||
forum->enumerateTopics([&](not_null<Data::ForumTopic*> topic) {
|
forum->enumerateTopics([&](not_null<ForumTopic*> topic) {
|
||||||
if (!topic->notify().settingsUnknown()) {
|
if (!topic->notify().settingsUnknown()) {
|
||||||
updateLocal(topic);
|
updateLocal(topic);
|
||||||
}
|
}
|
||||||
|
@ -266,11 +281,7 @@ auto NotifySettings::defaultValue(DefaultNotify type) const
|
||||||
|
|
||||||
const PeerNotifySettings &NotifySettings::defaultSettings(
|
const PeerNotifySettings &NotifySettings::defaultSettings(
|
||||||
not_null<const PeerData*> peer) const {
|
not_null<const PeerData*> peer) const {
|
||||||
return defaultSettings(peer->isUser()
|
return defaultSettings(DefaultNotifyType(peer));
|
||||||
? DefaultNotify::User
|
|
||||||
: (peer->isChat() || peer->isMegagroup())
|
|
||||||
? DefaultNotify::Group
|
|
||||||
: DefaultNotify::Broadcast);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const PeerNotifySettings &NotifySettings::defaultSettings(
|
const PeerNotifySettings &NotifySettings::defaultSettings(
|
||||||
|
@ -278,9 +289,16 @@ const PeerNotifySettings &NotifySettings::defaultSettings(
|
||||||
return defaultValue(type).settings;
|
return defaultValue(type).settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NotifySettings::isMuted(DefaultNotify type) const {
|
||||||
|
if (const auto until = defaultSettings(type).muteUntil()) {
|
||||||
|
return MutedFromUntil(*until, nullptr);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void NotifySettings::defaultUpdate(
|
void NotifySettings::defaultUpdate(
|
||||||
DefaultNotify type,
|
DefaultNotify type,
|
||||||
Data::MuteValue muteForSeconds,
|
MuteValue muteForSeconds,
|
||||||
std::optional<bool> silentPosts,
|
std::optional<bool> silentPosts,
|
||||||
std::optional<NotifySound> sound,
|
std::optional<NotifySound> sound,
|
||||||
std::optional<bool> storiesMuted) {
|
std::optional<bool> storiesMuted) {
|
||||||
|
@ -291,7 +309,7 @@ void NotifySettings::defaultUpdate(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotifySettings::updateLocal(not_null<Data::Thread*> thread) {
|
void NotifySettings::updateLocal(not_null<Thread*> thread) {
|
||||||
const auto topic = thread->asTopic();
|
const auto topic = thread->asTopic();
|
||||||
if (!topic) {
|
if (!topic) {
|
||||||
return updateLocal(thread->peer());
|
return updateLocal(thread->peer());
|
||||||
|
@ -351,7 +369,7 @@ void NotifySettings::cacheSound(not_null<DocumentData*> document) {
|
||||||
const auto view = document->createMediaView();
|
const auto view = document->createMediaView();
|
||||||
_ringtones.views.emplace(document->id, view);
|
_ringtones.views.emplace(document->id, view);
|
||||||
document->forceToCache(true);
|
document->forceToCache(true);
|
||||||
document->save(Data::FileOriginRingtones(), QString());
|
document->save(FileOriginRingtones(), QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotifySettings::cacheSound(const std::optional<NotifySound> &sound) {
|
void NotifySettings::cacheSound(const std::optional<NotifySound> &sound) {
|
||||||
|
@ -459,7 +477,7 @@ void NotifySettings::unmuteByFinished() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NotifySettings::isMuted(
|
bool NotifySettings::isMuted(
|
||||||
not_null<const Data::Thread*> thread,
|
not_null<const Thread*> thread,
|
||||||
crl::time *changesIn) const {
|
crl::time *changesIn) const {
|
||||||
const auto topic = thread->asTopic();
|
const auto topic = thread->asTopic();
|
||||||
const auto until = topic ? topic->notify().muteUntil() : std::nullopt;
|
const auto until = topic ? topic->notify().muteUntil() : std::nullopt;
|
||||||
|
@ -468,27 +486,24 @@ bool NotifySettings::isMuted(
|
||||||
: isMuted(thread->peer(), changesIn);
|
: isMuted(thread->peer(), changesIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NotifySettings::isMuted(not_null<const Data::Thread*> thread) const {
|
bool NotifySettings::isMuted(not_null<const Thread*> thread) const {
|
||||||
return isMuted(thread, nullptr);
|
return isMuted(thread, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
NotifySound NotifySettings::sound(
|
NotifySound NotifySettings::sound(not_null<const Thread*> thread) const {
|
||||||
not_null<const Data::Thread*> thread) const {
|
|
||||||
const auto topic = thread->asTopic();
|
const auto topic = thread->asTopic();
|
||||||
const auto sound = topic ? topic->notify().sound() : std::nullopt;
|
const auto sound = topic ? topic->notify().sound() : std::nullopt;
|
||||||
return sound ? *sound : this->sound(thread->peer());
|
return sound ? *sound : this->sound(thread->peer());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NotifySettings::muteUnknown(
|
bool NotifySettings::muteUnknown(not_null<const Thread*> thread) const {
|
||||||
not_null<const Data::Thread*> thread) const {
|
|
||||||
const auto topic = thread->asTopic();
|
const auto topic = thread->asTopic();
|
||||||
return (topic && topic->notify().settingsUnknown())
|
return (topic && topic->notify().settingsUnknown())
|
||||||
|| ((!topic || !topic->notify().muteUntil().has_value())
|
|| ((!topic || !topic->notify().muteUntil().has_value())
|
||||||
&& muteUnknown(thread->peer()));
|
&& muteUnknown(thread->peer()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NotifySettings::soundUnknown(
|
bool NotifySettings::soundUnknown(not_null<const Thread*> thread) const {
|
||||||
not_null<const Data::Thread*> thread) const {
|
|
||||||
const auto topic = thread->asTopic();
|
const auto topic = thread->asTopic();
|
||||||
return (topic && topic->notify().settingsUnknown())
|
return (topic && topic->notify().settingsUnknown())
|
||||||
|| ((!topic || !topic->notify().sound().has_value())
|
|| ((!topic || !topic->notify().sound().has_value())
|
||||||
|
@ -543,8 +558,7 @@ bool NotifySettings::silentPostsUnknown(
|
||||||
&& defaultSettings(peer).settingsUnknown());
|
&& defaultSettings(peer).settingsUnknown());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NotifySettings::soundUnknown(
|
bool NotifySettings::soundUnknown(not_null<const PeerData*> peer) const {
|
||||||
not_null<const PeerData*> peer) const {
|
|
||||||
return peer->notify().settingsUnknown()
|
return peer->notify().settingsUnknown()
|
||||||
|| (!peer->notify().sound().has_value()
|
|| (!peer->notify().sound().has_value()
|
||||||
&& defaultSettings(peer).settingsUnknown());
|
&& defaultSettings(peer).settingsUnknown());
|
||||||
|
@ -556,8 +570,7 @@ bool NotifySettings::settingsUnknown(not_null<const PeerData*> peer) const {
|
||||||
|| soundUnknown(peer);
|
|| soundUnknown(peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NotifySettings::settingsUnknown(
|
bool NotifySettings::settingsUnknown(not_null<const Thread*> thread) const {
|
||||||
not_null<const Data::Thread*> thread) const {
|
|
||||||
const auto topic = thread->asTopic();
|
const auto topic = thread->asTopic();
|
||||||
return muteUnknown(thread)
|
return muteUnknown(thread)
|
||||||
|| soundUnknown(thread)
|
|| soundUnknown(thread)
|
||||||
|
@ -577,4 +590,85 @@ rpl::producer<> NotifySettings::defaultUpdates(
|
||||||
: DefaultNotify::Broadcast);
|
: DefaultNotify::Broadcast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NotifySettings::loadExceptions() {
|
||||||
|
for (auto i = 0; i != kDefaultNotifyTypes; ++i) {
|
||||||
|
if (_exceptionsRequestId[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto type = static_cast<DefaultNotify>(i);
|
||||||
|
const auto api = &_owner->session().api();
|
||||||
|
const auto requestId = api->request(MTPaccount_GetNotifyExceptions(
|
||||||
|
MTP_flags(MTPaccount_GetNotifyExceptions::Flag::f_peer),
|
||||||
|
DefaultNotifyToMTP(type)
|
||||||
|
)).done([=](const MTPUpdates &result) {
|
||||||
|
api->applyUpdates(result);
|
||||||
|
}).send();
|
||||||
|
_exceptionsRequestId[i] = requestId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotifySettings::updateException(not_null<PeerData*> peer) {
|
||||||
|
const auto type = DefaultNotifyType(peer);
|
||||||
|
const auto index = static_cast<int>(type);
|
||||||
|
const auto exception = peer->notify().muteUntil().has_value();
|
||||||
|
if (!exception) {
|
||||||
|
if (_exceptions[index].remove(peer)) {
|
||||||
|
exceptionsUpdated(type);
|
||||||
|
}
|
||||||
|
} else if (SkipAddException(peer)) {
|
||||||
|
return;
|
||||||
|
} else if (_exceptions[index].emplace(peer).second) {
|
||||||
|
exceptionsUpdated(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotifySettings::exceptionsUpdated(DefaultNotify type) {
|
||||||
|
if (!ranges::contains(_exceptionsUpdatesScheduled, true)) {
|
||||||
|
crl::on_main(&_owner->session(), [=] {
|
||||||
|
const auto scheduled = base::take(_exceptionsUpdatesScheduled);
|
||||||
|
for (auto i = 0; i != kDefaultNotifyTypes; ++i) {
|
||||||
|
if (scheduled[i]) {
|
||||||
|
_exceptionsUpdates.fire(static_cast<DefaultNotify>(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_exceptionsUpdatesScheduled[static_cast<int>(type)] = true;
|
||||||
|
_exceptionsUpdatesRealtime.fire_copy(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<DefaultNotify> NotifySettings::exceptionsUpdates() const {
|
||||||
|
return _exceptionsUpdates.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto NotifySettings::exceptionsUpdatesRealtime() const
|
||||||
|
-> rpl::producer<DefaultNotify> {
|
||||||
|
return _exceptionsUpdatesRealtime.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
const base::flat_set<not_null<PeerData*>> &NotifySettings::exceptions(
|
||||||
|
DefaultNotify type) const {
|
||||||
|
const auto index = static_cast<int>(type);
|
||||||
|
Assert(index >= 0 && index < kDefaultNotifyTypes);
|
||||||
|
|
||||||
|
return _exceptions[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotifySettings::clearExceptions(DefaultNotify type) {
|
||||||
|
const auto index = static_cast<int>(type);
|
||||||
|
const auto list = base::take(_exceptions[index]);
|
||||||
|
if (list.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const auto &peer : list) {
|
||||||
|
// Duplicated in resetToDefault(peer / thread).
|
||||||
|
if (peer->notify().resetToDefault()) {
|
||||||
|
updateLocal(peer);
|
||||||
|
peer->session().api().updateNotifySettingsDelayed(peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Core::App().notifications().checkDelayed();
|
||||||
|
exceptionsUpdated(type);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
|
@ -26,13 +26,17 @@ enum class DefaultNotify {
|
||||||
Group,
|
Group,
|
||||||
Broadcast,
|
Broadcast,
|
||||||
};
|
};
|
||||||
|
[[nodiscard]] DefaultNotify DefaultNotifyType(
|
||||||
|
not_null<const PeerData*> peer);
|
||||||
|
|
||||||
|
[[nodiscard]] MTPInputNotifyPeer DefaultNotifyToMTP(DefaultNotify type);
|
||||||
|
|
||||||
class NotifySettings final {
|
class NotifySettings final {
|
||||||
public:
|
public:
|
||||||
NotifySettings(not_null<Session*> owner);
|
NotifySettings(not_null<Session*> owner);
|
||||||
|
|
||||||
void request(not_null<PeerData*> peer);
|
void request(not_null<PeerData*> peer);
|
||||||
void request(not_null<Data::Thread*> thread);
|
void request(not_null<Thread*> thread);
|
||||||
|
|
||||||
void apply(
|
void apply(
|
||||||
const MTPNotifyPeer ¬ifyPeer,
|
const MTPNotifyPeer ¬ifyPeer,
|
||||||
|
@ -50,25 +54,25 @@ public:
|
||||||
MsgId topicRootId,
|
MsgId topicRootId,
|
||||||
const MTPPeerNotifySettings &settings);
|
const MTPPeerNotifySettings &settings);
|
||||||
void apply(
|
void apply(
|
||||||
not_null<Data::ForumTopic*> topic,
|
not_null<ForumTopic*> topic,
|
||||||
const MTPPeerNotifySettings &settings);
|
const MTPPeerNotifySettings &settings);
|
||||||
|
|
||||||
void update(
|
void update(
|
||||||
not_null<Data::Thread*> thread,
|
not_null<Thread*> thread,
|
||||||
Data::MuteValue muteForSeconds,
|
MuteValue muteForSeconds,
|
||||||
std::optional<bool> silentPosts = std::nullopt,
|
std::optional<bool> silentPosts = std::nullopt,
|
||||||
std::optional<NotifySound> sound = std::nullopt,
|
std::optional<NotifySound> sound = std::nullopt,
|
||||||
std::optional<bool> storiesMuted = std::nullopt);
|
std::optional<bool> storiesMuted = std::nullopt);
|
||||||
void resetToDefault(not_null<Data::Thread*> thread);
|
void resetToDefault(not_null<Thread*> thread);
|
||||||
void update(
|
void update(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
Data::MuteValue muteForSeconds,
|
MuteValue muteForSeconds,
|
||||||
std::optional<bool> silentPosts = std::nullopt,
|
std::optional<bool> silentPosts = std::nullopt,
|
||||||
std::optional<NotifySound> sound = std::nullopt,
|
std::optional<NotifySound> sound = std::nullopt,
|
||||||
std::optional<bool> storiesMuted = std::nullopt);
|
std::optional<bool> storiesMuted = std::nullopt);
|
||||||
void resetToDefault(not_null<PeerData*> peer);
|
void resetToDefault(not_null<PeerData*> peer);
|
||||||
|
|
||||||
void forumParentMuteUpdated(not_null<Data::Forum*> forum);
|
void forumParentMuteUpdated(not_null<Forum*> forum);
|
||||||
|
|
||||||
void cacheSound(DocumentId id);
|
void cacheSound(DocumentId id);
|
||||||
void cacheSound(not_null<DocumentData*> document);
|
void cacheSound(not_null<DocumentData*> document);
|
||||||
|
@ -81,21 +85,19 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] const PeerNotifySettings &defaultSettings(
|
[[nodiscard]] const PeerNotifySettings &defaultSettings(
|
||||||
DefaultNotify type) const;
|
DefaultNotify type) const;
|
||||||
|
[[nodiscard]] bool isMuted(DefaultNotify type) const;
|
||||||
|
|
||||||
void defaultUpdate(
|
void defaultUpdate(
|
||||||
DefaultNotify type,
|
DefaultNotify type,
|
||||||
Data::MuteValue muteForSeconds,
|
MuteValue muteForSeconds,
|
||||||
std::optional<bool> silentPosts = std::nullopt,
|
std::optional<bool> silentPosts = std::nullopt,
|
||||||
std::optional<NotifySound> sound = std::nullopt,
|
std::optional<NotifySound> sound = std::nullopt,
|
||||||
std::optional<bool> storiesMuted = std::nullopt);
|
std::optional<bool> storiesMuted = std::nullopt);
|
||||||
|
|
||||||
[[nodiscard]] bool isMuted(not_null<const Data::Thread*> thread) const;
|
[[nodiscard]] bool isMuted(not_null<const Thread*> thread) const;
|
||||||
[[nodiscard]] NotifySound sound(
|
[[nodiscard]] NotifySound sound(not_null<const Thread*> thread) const;
|
||||||
not_null<const Data::Thread*> thread) const;
|
[[nodiscard]] bool muteUnknown(not_null<const Thread*> thread) const;
|
||||||
[[nodiscard]] bool muteUnknown(
|
[[nodiscard]] bool soundUnknown(not_null<const Thread*> thread) const;
|
||||||
not_null<const Data::Thread*> thread) const;
|
|
||||||
[[nodiscard]] bool soundUnknown(
|
|
||||||
not_null<const Data::Thread*> thread) const;
|
|
||||||
|
|
||||||
[[nodiscard]] bool isMuted(not_null<const PeerData*> peer) const;
|
[[nodiscard]] bool isMuted(not_null<const PeerData*> peer) const;
|
||||||
[[nodiscard]] bool silentPosts(not_null<const PeerData*> peer) const;
|
[[nodiscard]] bool silentPosts(not_null<const PeerData*> peer) const;
|
||||||
|
@ -105,7 +107,17 @@ public:
|
||||||
not_null<const PeerData*> peer) const;
|
not_null<const PeerData*> peer) const;
|
||||||
[[nodiscard]] bool soundUnknown(not_null<const PeerData*> peer) const;
|
[[nodiscard]] bool soundUnknown(not_null<const PeerData*> peer) const;
|
||||||
|
|
||||||
|
void loadExceptions();
|
||||||
|
[[nodiscard]] rpl::producer<DefaultNotify> exceptionsUpdates() const;
|
||||||
|
[[nodiscard]] auto exceptionsUpdatesRealtime() const
|
||||||
|
-> rpl::producer<DefaultNotify>;
|
||||||
|
[[nodiscard]] const base::flat_set<not_null<PeerData*>> &exceptions(
|
||||||
|
DefaultNotify type) const;
|
||||||
|
void clearExceptions(DefaultNotify type);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static constexpr auto kDefaultNotifyTypes = 3;
|
||||||
|
|
||||||
struct DefaultValue {
|
struct DefaultValue {
|
||||||
PeerNotifySettings settings;
|
PeerNotifySettings settings;
|
||||||
rpl::event_stream<> updates;
|
rpl::event_stream<> updates;
|
||||||
|
@ -114,7 +126,7 @@ private:
|
||||||
void cacheSound(const std::optional<NotifySound> &sound);
|
void cacheSound(const std::optional<NotifySound> &sound);
|
||||||
|
|
||||||
[[nodiscard]] bool isMuted(
|
[[nodiscard]] bool isMuted(
|
||||||
not_null<const Data::Thread*> thread,
|
not_null<const Thread*> thread,
|
||||||
crl::time *changesIn) const;
|
crl::time *changesIn) const;
|
||||||
[[nodiscard]] bool isMuted(
|
[[nodiscard]] bool isMuted(
|
||||||
not_null<const PeerData*> peer,
|
not_null<const PeerData*> peer,
|
||||||
|
@ -126,21 +138,22 @@ private:
|
||||||
not_null<const PeerData*> peer) const;
|
not_null<const PeerData*> peer) const;
|
||||||
[[nodiscard]] bool settingsUnknown(not_null<const PeerData*> peer) const;
|
[[nodiscard]] bool settingsUnknown(not_null<const PeerData*> peer) const;
|
||||||
[[nodiscard]] bool settingsUnknown(
|
[[nodiscard]] bool settingsUnknown(
|
||||||
not_null<const Data::Thread*> thread) const;
|
not_null<const Thread*> thread) const;
|
||||||
|
|
||||||
void unmuteByFinished();
|
void unmuteByFinished();
|
||||||
void unmuteByFinishedDelayed(crl::time delay);
|
void unmuteByFinishedDelayed(crl::time delay);
|
||||||
void updateLocal(not_null<Data::Thread*> thread);
|
void updateLocal(not_null<Thread*> thread);
|
||||||
void updateLocal(not_null<PeerData*> peer);
|
void updateLocal(not_null<PeerData*> peer);
|
||||||
void updateLocal(DefaultNotify type);
|
void updateLocal(DefaultNotify type);
|
||||||
|
|
||||||
|
void updateException(not_null<PeerData*> peer);
|
||||||
|
void exceptionsUpdated(DefaultNotify type);
|
||||||
|
|
||||||
const not_null<Session*> _owner;
|
const not_null<Session*> _owner;
|
||||||
|
|
||||||
DefaultValue _defaultValues[3];
|
DefaultValue _defaultValues[3];
|
||||||
std::unordered_set<not_null<const PeerData*>> _mutedPeers;
|
std::unordered_set<not_null<const PeerData*>> _mutedPeers;
|
||||||
std::unordered_map<
|
std::unordered_map<not_null<ForumTopic*>, rpl::lifetime> _mutedTopics;
|
||||||
not_null<Data::ForumTopic*>,
|
|
||||||
rpl::lifetime> _mutedTopics;
|
|
||||||
base::Timer _unmuteByFinishedTimer;
|
base::Timer _unmuteByFinishedTimer;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
@ -151,6 +164,14 @@ private:
|
||||||
rpl::lifetime pendingLifetime;
|
rpl::lifetime pendingLifetime;
|
||||||
} _ringtones;
|
} _ringtones;
|
||||||
|
|
||||||
|
rpl::event_stream<DefaultNotify> _exceptionsUpdates;
|
||||||
|
rpl::event_stream<DefaultNotify> _exceptionsUpdatesRealtime;
|
||||||
|
std::array<
|
||||||
|
base::flat_set<not_null<PeerData*>>,
|
||||||
|
kDefaultNotifyTypes> _exceptions;
|
||||||
|
std::array<mtpRequestId, kDefaultNotifyTypes> _exceptionsRequestId = {};
|
||||||
|
std::array<bool, kDefaultNotifyTypes> _exceptionsUpdatesScheduled = {};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
|
@ -256,6 +256,15 @@ bool PeerNotifySettings::change(
|
||||||
SerializeSound(std::nullopt))); // stories_sound
|
SerializeSound(std::nullopt))); // stories_sound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PeerNotifySettings::resetToDefault() {
|
||||||
|
if (_known && !_value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_known = true;
|
||||||
|
_value = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<TimeId> PeerNotifySettings::muteUntil() const {
|
std::optional<TimeId> PeerNotifySettings::muteUntil() const {
|
||||||
return _value
|
return _value
|
||||||
? _value->muteUntil()
|
? _value->muteUntil()
|
||||||
|
|
|
@ -46,6 +46,7 @@ public:
|
||||||
std::optional<bool> silentPosts,
|
std::optional<bool> silentPosts,
|
||||||
std::optional<NotifySound> sound,
|
std::optional<NotifySound> sound,
|
||||||
std::optional<bool> storiesMuted);
|
std::optional<bool> storiesMuted);
|
||||||
|
bool resetToDefault();
|
||||||
|
|
||||||
bool settingsUnknown() const;
|
bool settingsUnknown() const;
|
||||||
std::optional<TimeId> muteUntil() const;
|
std::optional<TimeId> muteUntil() const;
|
||||||
|
|
|
@ -23,6 +23,12 @@ DialogRow {
|
||||||
unreadMarkDiameter: pixels;
|
unreadMarkDiameter: pixels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThreeStateIcon {
|
||||||
|
icon: icon;
|
||||||
|
over: icon;
|
||||||
|
active: icon;
|
||||||
|
}
|
||||||
|
|
||||||
ForumTopicIcon {
|
ForumTopicIcon {
|
||||||
size: pixels;
|
size: pixels;
|
||||||
font: font;
|
font: font;
|
||||||
|
@ -316,38 +322,56 @@ dialogSearchFrom: IconButton(dialogCalendar) {
|
||||||
}
|
}
|
||||||
|
|
||||||
dialogsChatTypeSkip: 3px;
|
dialogsChatTypeSkip: 3px;
|
||||||
dialogsChatIcon: icon {{ "dialogs/dialogs_chat", dialogsChatIconFg, point(1px, 4px) }};
|
dialogsChatIcon: ThreeStateIcon {
|
||||||
dialogsChatIconOver: icon {{ "dialogs/dialogs_chat", dialogsChatIconFgOver, point(1px, 4px) }};
|
icon: icon {{ "dialogs/dialogs_chat", dialogsChatIconFg, point(1px, 4px) }};
|
||||||
dialogsChatIconActive: icon {{ "dialogs/dialogs_chat", dialogsChatIconFgActive, point(1px, 4px) }};
|
over: icon {{ "dialogs/dialogs_chat", dialogsChatIconFgOver, point(1px, 4px) }};
|
||||||
dialogsChannelIcon: icon {{ "dialogs/dialogs_channel", dialogsChatIconFg, point(3px, 4px) }};
|
active: icon {{ "dialogs/dialogs_chat", dialogsChatIconFgActive, point(1px, 4px) }};
|
||||||
dialogsChannelIconOver: icon {{ "dialogs/dialogs_channel", dialogsChatIconFgOver, point(3px, 4px) }};
|
}
|
||||||
dialogsChannelIconActive: icon {{ "dialogs/dialogs_channel", dialogsChatIconFgActive, point(3px, 4px) }};
|
dialogsChannelIcon: ThreeStateIcon {
|
||||||
dialogsBotIcon: icon {{ "dialogs/dialogs_bot", dialogsChatIconFg, point(1px, 3px) }};
|
icon: icon {{ "dialogs/dialogs_channel", dialogsChatIconFg, point(3px, 4px) }};
|
||||||
dialogsBotIconOver: icon {{ "dialogs/dialogs_bot", dialogsChatIconFgOver, point(1px, 3px) }};
|
over: icon {{ "dialogs/dialogs_channel", dialogsChatIconFgOver, point(3px, 4px) }};
|
||||||
dialogsBotIconActive: icon {{ "dialogs/dialogs_bot", dialogsChatIconFgActive, point(1px, 3px) }};
|
active: icon {{ "dialogs/dialogs_channel", dialogsChatIconFgActive, point(3px, 4px) }};
|
||||||
dialogsForumIcon: icon {{ "dialogs/dialogs_forum", dialogsChatIconFg, point(1px, 4px) }};
|
}
|
||||||
dialogsForumIconOver: icon {{ "dialogs/dialogs_forum", dialogsChatIconFgOver, point(1px, 4px) }};
|
dialogsBotIcon: ThreeStateIcon {
|
||||||
dialogsForumIconActive: icon {{ "dialogs/dialogs_forum", dialogsChatIconFgActive, point(1px, 4px) }};
|
icon: icon {{ "dialogs/dialogs_bot", dialogsChatIconFg, point(1px, 3px) }};
|
||||||
|
over: icon {{ "dialogs/dialogs_bot", dialogsChatIconFgOver, point(1px, 3px) }};
|
||||||
|
active: icon {{ "dialogs/dialogs_bot", dialogsChatIconFgActive, point(1px, 3px) }};
|
||||||
|
}
|
||||||
|
dialogsForumIcon: ThreeStateIcon {
|
||||||
|
icon: icon {{ "dialogs/dialogs_forum", dialogsChatIconFg, point(1px, 4px) }};
|
||||||
|
over: icon {{ "dialogs/dialogs_forum", dialogsChatIconFgOver, point(1px, 4px) }};
|
||||||
|
active: icon {{ "dialogs/dialogs_forum", dialogsChatIconFgActive, point(1px, 4px) }};
|
||||||
|
}
|
||||||
dialogsArchiveUserpic: icon {{ "archive_userpic", historyPeerUserpicFg }};
|
dialogsArchiveUserpic: icon {{ "archive_userpic", historyPeerUserpicFg }};
|
||||||
dialogsRepliesUserpic: icon {{ "replies_userpic", historyPeerUserpicFg }};
|
dialogsRepliesUserpic: icon {{ "replies_userpic", historyPeerUserpicFg }};
|
||||||
dialogsInaccessibleUserpic: icon {{ "dialogs/inaccessible_userpic", historyPeerUserpicFg }};
|
dialogsInaccessibleUserpic: icon {{ "dialogs/inaccessible_userpic", historyPeerUserpicFg }};
|
||||||
|
|
||||||
dialogsSendStateSkip: 20px;
|
dialogsSendStateSkip: 20px;
|
||||||
dialogsSendingIcon: icon {{ "dialogs/dialogs_sending", dialogsSendingIconFg, point(8px, 4px) }};
|
dialogsSendingIcon: ThreeStateIcon {
|
||||||
dialogsSendingIconOver: icon {{ "dialogs/dialogs_sending", dialogsSendingIconFgOver, point(8px, 4px) }};
|
icon: icon {{ "dialogs/dialogs_sending", dialogsSendingIconFg, point(8px, 4px) }};
|
||||||
dialogsSendingIconActive: icon {{ "dialogs/dialogs_sending", dialogsSendingIconFgActive, point(8px, 4px) }};
|
over: icon {{ "dialogs/dialogs_sending", dialogsSendingIconFgOver, point(8px, 4px) }};
|
||||||
dialogsSentIcon: icon {{ "dialogs/dialogs_sent", dialogsSentIconFg, point(10px, 4px) }};
|
active: icon {{ "dialogs/dialogs_sending", dialogsSendingIconFgActive, point(8px, 4px) }};
|
||||||
dialogsSentIconOver: icon {{ "dialogs/dialogs_sent", dialogsSentIconFgOver, point(10px, 4px) }};
|
}
|
||||||
dialogsSentIconActive: icon {{ "dialogs/dialogs_sent", dialogsSentIconFgActive, point(10px, 4px) }};
|
dialogsSentIcon: ThreeStateIcon {
|
||||||
dialogsReceivedIcon: icon {{ "dialogs/dialogs_received", dialogsSentIconFg, point(5px, 4px) }};
|
icon: icon {{ "dialogs/dialogs_sent", dialogsSentIconFg, point(10px, 4px) }};
|
||||||
dialogsReceivedIconOver: icon {{ "dialogs/dialogs_received", dialogsSentIconFgOver, point(5px, 4px) }};
|
over: icon {{ "dialogs/dialogs_sent", dialogsSentIconFgOver, point(10px, 4px) }};
|
||||||
dialogsReceivedIconActive: icon {{ "dialogs/dialogs_received", dialogsSentIconFgActive, point(5px, 4px) }};
|
active: icon {{ "dialogs/dialogs_sent", dialogsSentIconFgActive, point(10px, 4px) }};
|
||||||
dialogsPinnedIcon: icon {{ "dialogs/dialogs_pinned", dialogsUnreadBgMuted }};
|
}
|
||||||
dialogsPinnedIconOver: icon {{ "dialogs/dialogs_pinned", dialogsUnreadBgMutedOver }};
|
dialogsReceivedIcon: ThreeStateIcon {
|
||||||
dialogsPinnedIconActive: icon {{ "dialogs/dialogs_pinned", dialogsUnreadBgMutedActive }};
|
icon: icon {{ "dialogs/dialogs_received", dialogsSentIconFg, point(5px, 4px) }};
|
||||||
dialogsLockIcon: icon {{ "emoji/premium_lock", dialogsUnreadBgMuted, point(4px, 0px) }};
|
over: icon {{ "dialogs/dialogs_received", dialogsSentIconFgOver, point(5px, 4px) }};
|
||||||
dialogsLockIconOver: icon {{ "emoji/premium_lock", dialogsUnreadBgMutedOver, point(4px, 0px) }};
|
active: icon {{ "dialogs/dialogs_received", dialogsSentIconFgActive, point(5px, 4px) }};
|
||||||
dialogsLockIconActive: icon {{ "emoji/premium_lock", dialogsUnreadBgMutedActive, point(4px, 0px) }};
|
}
|
||||||
|
dialogsPinnedIcon: ThreeStateIcon {
|
||||||
|
icon: icon {{ "dialogs/dialogs_pinned", dialogsUnreadBgMuted }};
|
||||||
|
over: icon {{ "dialogs/dialogs_pinned", dialogsUnreadBgMutedOver }};
|
||||||
|
active: icon {{ "dialogs/dialogs_pinned", dialogsUnreadBgMutedActive }};
|
||||||
|
}
|
||||||
|
dialogsLockIcon: ThreeStateIcon {
|
||||||
|
icon: icon {{ "emoji/premium_lock", dialogsUnreadBgMuted, point(4px, 0px) }};
|
||||||
|
over: icon {{ "emoji/premium_lock", dialogsUnreadBgMutedOver, point(4px, 0px) }};
|
||||||
|
active: icon {{ "emoji/premium_lock", dialogsUnreadBgMutedActive, point(4px, 0px) }};
|
||||||
|
}
|
||||||
|
|
||||||
dialogsVerifiedIcon: icon {
|
dialogsVerifiedIcon: icon {
|
||||||
{ "dialogs/dialogs_verified_star", dialogsVerifiedIconBg },
|
{ "dialogs/dialogs_verified_star", dialogsVerifiedIconBg },
|
||||||
|
@ -361,9 +385,11 @@ dialogsVerifiedIconActive: icon {
|
||||||
{ "dialogs/dialogs_verified_star", dialogsVerifiedIconBgActive },
|
{ "dialogs/dialogs_verified_star", dialogsVerifiedIconBgActive },
|
||||||
{ "dialogs/dialogs_verified_check", dialogsVerifiedIconFgActive },
|
{ "dialogs/dialogs_verified_check", dialogsVerifiedIconFgActive },
|
||||||
};
|
};
|
||||||
dialogsPremiumIcon: icon {{ "dialogs/dialogs_premium", dialogsVerifiedIconBg }};
|
dialogsPremiumIcon: ThreeStateIcon {
|
||||||
dialogsPremiumIconOver: icon {{ "dialogs/dialogs_premium", dialogsVerifiedIconBgOver }};
|
icon: icon {{ "dialogs/dialogs_premium", dialogsVerifiedIconBg }};
|
||||||
dialogsPremiumIconActive: icon {{ "dialogs/dialogs_premium", dialogsVerifiedIconBgActive }};
|
over: icon {{ "dialogs/dialogs_premium", dialogsVerifiedIconBgOver }};
|
||||||
|
active: icon {{ "dialogs/dialogs_premium", dialogsVerifiedIconBgActive }};
|
||||||
|
}
|
||||||
|
|
||||||
historySendingIcon: icon {{ "dialogs/dialogs_sending", historySendingOutIconFg, point(5px, 5px) }};
|
historySendingIcon: icon {{ "dialogs/dialogs_sending", historySendingOutIconFg, point(5px, 5px) }};
|
||||||
historySendingInvertedIcon: icon {{ "dialogs/dialogs_sending", historySendingInvertedIconFg, point(5px, 5px) }};
|
historySendingInvertedIcon: icon {{ "dialogs/dialogs_sending", historySendingInvertedIconFg, point(5px, 5px) }};
|
||||||
|
@ -436,12 +462,28 @@ dialogsMiniPreviewSkip: 2px;
|
||||||
dialogsMiniPreviewRight: 3px;
|
dialogsMiniPreviewRight: 3px;
|
||||||
dialogsMiniPlay: icon{{ "dialogs/dialogs_mini_play", videoPlayIconFg }};
|
dialogsMiniPlay: icon{{ "dialogs/dialogs_mini_play", videoPlayIconFg }};
|
||||||
|
|
||||||
dialogsUnreadMention: icon{{ "dialogs/dialogs_mention", dialogsUnreadFg }};
|
dialogsMiniForwardIcon: ThreeStateIcon {
|
||||||
dialogsUnreadMentionOver: icon{{ "dialogs/dialogs_mention", dialogsUnreadFgOver }};
|
icon: icon {{ "mini_forward", dialogsTextFg, point(0px, 1px) }};
|
||||||
dialogsUnreadMentionActive: icon{{ "dialogs/dialogs_mention", dialogsUnreadFgActive }};
|
over: icon {{ "mini_forward", dialogsTextFgOver, point(0px, 1px) }};
|
||||||
dialogsUnreadReaction: icon{{ "dialogs/dialogs_reaction", dialogsUnreadFg }};
|
active: icon {{ "mini_forward", dialogsTextFgActive, point(0px, 1px) }};
|
||||||
dialogsUnreadReactionOver: icon{{ "dialogs/dialogs_reaction", dialogsUnreadFgOver }};
|
}
|
||||||
dialogsUnreadReactionActive: icon{{ "dialogs/dialogs_reaction", dialogsUnreadFgActive }};
|
dialogsMiniIconSkip: 2px;
|
||||||
|
dialogsMiniReplyStoryIcon: ThreeStateIcon {
|
||||||
|
icon: icon {{ "mini_reply_story", dialogsTextFg, point(0px, 1px) }};
|
||||||
|
over: icon {{ "mini_reply_story", dialogsTextFgOver, point(0px, 1px) }};
|
||||||
|
active: icon {{ "mini_reply_story", dialogsTextFgActive, point(0px, 1px) }};
|
||||||
|
}
|
||||||
|
|
||||||
|
dialogsUnreadMention: ThreeStateIcon {
|
||||||
|
icon: icon{{ "dialogs/dialogs_mention", dialogsUnreadFg }};
|
||||||
|
over: icon{{ "dialogs/dialogs_mention", dialogsUnreadFgOver }};
|
||||||
|
active: icon{{ "dialogs/dialogs_mention", dialogsUnreadFgActive }};
|
||||||
|
}
|
||||||
|
dialogsUnreadReaction: ThreeStateIcon {
|
||||||
|
icon: icon{{ "dialogs/dialogs_reaction", dialogsUnreadFg }};
|
||||||
|
over: icon{{ "dialogs/dialogs_reaction", dialogsUnreadFgOver }};
|
||||||
|
active: icon{{ "dialogs/dialogs_reaction", dialogsUnreadFgActive }};
|
||||||
|
}
|
||||||
|
|
||||||
downloadBarHeight: 46px;
|
downloadBarHeight: 46px;
|
||||||
downloadArrow: icon{{ "fast_to_original", menuIconFg }};
|
downloadArrow: icon{{ "fast_to_original", menuIconFg }};
|
||||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "dialogs/dialogs_inner_widget.h"
|
#include "dialogs/dialogs_inner_widget.h"
|
||||||
|
|
||||||
|
#include "dialogs/dialogs_three_state_icon.h"
|
||||||
#include "dialogs/ui/dialogs_layout.h"
|
#include "dialogs/ui/dialogs_layout.h"
|
||||||
#include "dialogs/ui/dialogs_stories_content.h"
|
#include "dialogs/ui/dialogs_stories_content.h"
|
||||||
#include "dialogs/ui/dialogs_stories_list.h"
|
#include "dialogs/ui/dialogs_stories_list.h"
|
||||||
|
@ -1010,11 +1011,10 @@ void InnerWidget::paintPeerSearchResult(
|
||||||
: context.selected
|
: context.selected
|
||||||
? &st::dialogsVerifiedIconOver
|
? &st::dialogsVerifiedIconOver
|
||||||
: &st::dialogsVerifiedIcon),
|
: &st::dialogsVerifiedIcon),
|
||||||
.premium = (context.active
|
.premium = &ThreeStateIcon(
|
||||||
? &st::dialogsPremiumIconActive
|
st::dialogsPremiumIcon,
|
||||||
: context.selected
|
context.active,
|
||||||
? &st::dialogsPremiumIconOver
|
context.selected),
|
||||||
: &st::dialogsPremiumIcon),
|
|
||||||
.scam = (context.active
|
.scam = (context.active
|
||||||
? &st::dialogsScamFgActive
|
? &st::dialogsScamFgActive
|
||||||
: context.selected
|
: context.selected
|
||||||
|
|
|
@ -194,6 +194,11 @@ UnreadState MainList::unreadState() const {
|
||||||
result.chatsMuted = result.chats;
|
result.chatsMuted = result.chats;
|
||||||
result.marksMuted = result.marks;
|
result.marksMuted = result.marks;
|
||||||
}
|
}
|
||||||
|
volatile auto touch = _unreadState.marks + _unreadState.marksMuted
|
||||||
|
+ _unreadState.messages + _unreadState.messagesMuted
|
||||||
|
+ _unreadState.chats + _unreadState.chatsMuted
|
||||||
|
+ _unreadState.reactions + _unreadState.reactionsMuted
|
||||||
|
+ _unreadState.mentions;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
21
Telegram/SourceFiles/dialogs/dialogs_three_state_icon.h
Normal file
21
Telegram/SourceFiles/dialogs/dialogs_three_state_icon.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
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 "styles/style_dialogs.h"
|
||||||
|
|
||||||
|
namespace Dialogs {
|
||||||
|
|
||||||
|
[[nodiscard]] inline const style::icon &ThreeStateIcon(
|
||||||
|
const style::ThreeStateIcon &icons,
|
||||||
|
bool active,
|
||||||
|
bool over) {
|
||||||
|
return active ? icons.active : over ? icons.over : icons.icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Dialogs
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_forum_topic.h"
|
#include "data/data_forum_topic.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "dialogs/dialogs_list.h"
|
#include "dialogs/dialogs_list.h"
|
||||||
|
#include "dialogs/dialogs_three_state_icon.h"
|
||||||
#include "dialogs/ui/dialogs_video_userpic.h"
|
#include "dialogs/ui/dialogs_video_userpic.h"
|
||||||
#include "styles/style_dialogs.h"
|
#include "styles/style_dialogs.h"
|
||||||
#include "styles/style_window.h"
|
#include "styles/style_window.h"
|
||||||
|
@ -147,11 +148,10 @@ int PaintBadges(
|
||||||
const auto badge = PaintUnreadBadge(p, counter, right, top, st);
|
const auto badge = PaintUnreadBadge(p, counter, right, top, st);
|
||||||
right -= badge.width() + st.padding;
|
right -= badge.width() + st.padding;
|
||||||
} else if (displayPinnedIcon) {
|
} else if (displayPinnedIcon) {
|
||||||
const auto &icon = context.active
|
const auto &icon = ThreeStateIcon(
|
||||||
? st::dialogsPinnedIconActive
|
st::dialogsPinnedIcon,
|
||||||
: context.selected
|
context.active,
|
||||||
? st::dialogsPinnedIconOver
|
context.selected);
|
||||||
: st::dialogsPinnedIcon;
|
|
||||||
icon.paint(p, right - icon.width(), pinnedIconTop, context.width);
|
icon.paint(p, right - icon.width(), pinnedIconTop, context.width);
|
||||||
right -= icon.width() + st::dialogsUnreadPadding;
|
right -= icon.width() + st::dialogsUnreadPadding;
|
||||||
}
|
}
|
||||||
|
@ -169,17 +169,12 @@ int PaintBadges(
|
||||||
st.textTop = 0;
|
st.textTop = 0;
|
||||||
const auto counter = QString();
|
const auto counter = QString();
|
||||||
const auto badge = PaintUnreadBadge(p, counter, right, top, st);
|
const auto badge = PaintUnreadBadge(p, counter, right, top, st);
|
||||||
(badgesState.mention
|
ThreeStateIcon(
|
||||||
? (st.active
|
badgesState.mention
|
||||||
? st::dialogsUnreadMentionActive
|
? st::dialogsUnreadMention
|
||||||
: st.selected
|
: st::dialogsUnreadReaction,
|
||||||
? st::dialogsUnreadMentionOver
|
st.active,
|
||||||
: st::dialogsUnreadMention)
|
st.selected).paintInCenter(p, badge);
|
||||||
: (st.active
|
|
||||||
? st::dialogsUnreadReactionActive
|
|
||||||
: st.selected
|
|
||||||
? st::dialogsUnreadReactionOver
|
|
||||||
: st::dialogsUnreadReaction)).paintInCenter(p, badge);
|
|
||||||
right -= badge.width() + st.padding + st::dialogsUnreadPadding;
|
right -= badge.width() + st.padding + st::dialogsUnreadPadding;
|
||||||
}
|
}
|
||||||
return (initial - right);
|
return (initial - right);
|
||||||
|
@ -437,11 +432,10 @@ void PaintRow(
|
||||||
auto availableWidth = namewidth;
|
auto availableWidth = namewidth;
|
||||||
if (entry->isPinnedDialog(context.filter)
|
if (entry->isPinnedDialog(context.filter)
|
||||||
&& (context.filter || !entry->fixedOnTopIndex())) {
|
&& (context.filter || !entry->fixedOnTopIndex())) {
|
||||||
auto &icon = context.active
|
auto &icon = ThreeStateIcon(
|
||||||
? st::dialogsPinnedIconActive
|
st::dialogsPinnedIcon,
|
||||||
: context.selected
|
context.active,
|
||||||
? st::dialogsPinnedIconOver
|
context.selected);
|
||||||
: st::dialogsPinnedIcon;
|
|
||||||
icon.paint(
|
icon.paint(
|
||||||
p,
|
p,
|
||||||
context.width - context.st->padding.right() - icon.width(),
|
context.width - context.st->padding.right() - icon.width(),
|
||||||
|
@ -527,11 +521,10 @@ void PaintRow(
|
||||||
auto availableWidth = namewidth;
|
auto availableWidth = namewidth;
|
||||||
if (entry->isPinnedDialog(context.filter)
|
if (entry->isPinnedDialog(context.filter)
|
||||||
&& (context.filter || !entry->fixedOnTopIndex())) {
|
&& (context.filter || !entry->fixedOnTopIndex())) {
|
||||||
auto &icon = context.active
|
auto &icon = ThreeStateIcon(
|
||||||
? st::dialogsPinnedIconActive
|
st::dialogsPinnedIcon,
|
||||||
: context.selected
|
context.active,
|
||||||
? st::dialogsPinnedIconOver
|
context.selected);
|
||||||
: st::dialogsPinnedIcon;
|
|
||||||
icon.paint(p, context.width - context.st->padding.right() - icon.width(), texttop, context.width);
|
icon.paint(p, context.width - context.st->padding.right() - icon.width(), texttop, context.width);
|
||||||
availableWidth -= icon.width() + st::dialogsUnreadPadding;
|
availableWidth -= icon.width() + st::dialogsUnreadPadding;
|
||||||
}
|
}
|
||||||
|
@ -561,51 +554,49 @@ void PaintRow(
|
||||||
paintItemCallback(nameleft, namewidth);
|
paintItemCallback(nameleft, namewidth);
|
||||||
} else if (entry->isPinnedDialog(context.filter)
|
} else if (entry->isPinnedDialog(context.filter)
|
||||||
&& (context.filter || !entry->fixedOnTopIndex())) {
|
&& (context.filter || !entry->fixedOnTopIndex())) {
|
||||||
auto &icon = context.active
|
auto &icon = ThreeStateIcon(
|
||||||
? st::dialogsPinnedIconActive
|
st::dialogsPinnedIcon,
|
||||||
: context.selected
|
context.active,
|
||||||
? st::dialogsPinnedIconOver
|
context.selected);
|
||||||
: st::dialogsPinnedIcon;
|
icon.paint(
|
||||||
icon.paint(p, context.width - context.st->padding.right() - icon.width(), texttop, context.width);
|
p,
|
||||||
|
context.width - context.st->padding.right() - icon.width(),
|
||||||
|
texttop,
|
||||||
|
context.width);
|
||||||
}
|
}
|
||||||
const auto sendStateIcon = [&]() -> const style::icon* {
|
const auto sendStateIcon = [&]() -> const style::icon* {
|
||||||
if (!thread) {
|
if (!thread) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
} else if (const auto topic = thread->asTopic()
|
} else if (const auto topic = thread->asTopic()
|
||||||
; !context.search && topic && topic->closed()) {
|
; !context.search && topic && topic->closed()) {
|
||||||
return &(context.active
|
return &ThreeStateIcon(
|
||||||
? st::dialogsLockIconActive
|
st::dialogsLockIcon,
|
||||||
: context.selected
|
context.active,
|
||||||
? st::dialogsLockIconOver
|
context.selected);
|
||||||
: st::dialogsLockIcon);
|
|
||||||
} else if (draft) {
|
} else if (draft) {
|
||||||
if (draft->saveRequestId) {
|
if (draft->saveRequestId) {
|
||||||
return &(context.active
|
return &ThreeStateIcon(
|
||||||
? st::dialogsSendingIconActive
|
st::dialogsSendingIcon,
|
||||||
: context.selected
|
context.active,
|
||||||
? st::dialogsSendingIconOver
|
context.selected);
|
||||||
: st::dialogsSendingIcon);
|
|
||||||
}
|
}
|
||||||
} else if (item && !item->isEmpty() && item->needCheck()) {
|
} else if (item && !item->isEmpty() && item->needCheck()) {
|
||||||
if (!item->isSending() && !item->hasFailed()) {
|
if (!item->isSending() && !item->hasFailed()) {
|
||||||
if (item->unread(thread)) {
|
if (item->unread(thread)) {
|
||||||
return &(context.active
|
return &ThreeStateIcon(
|
||||||
? st::dialogsSentIconActive
|
st::dialogsSentIcon,
|
||||||
: context.selected
|
context.active,
|
||||||
? st::dialogsSentIconOver
|
context.selected);
|
||||||
: st::dialogsSentIcon);
|
|
||||||
}
|
}
|
||||||
return &(context.active
|
return &ThreeStateIcon(
|
||||||
? st::dialogsReceivedIconActive
|
st::dialogsReceivedIcon,
|
||||||
: context.selected
|
context.active,
|
||||||
? st::dialogsReceivedIconOver
|
context.selected);
|
||||||
: st::dialogsReceivedIcon);
|
|
||||||
}
|
}
|
||||||
return &(context.active
|
return &ThreeStateIcon(
|
||||||
? st::dialogsSendingIconActive
|
st::dialogsSendingIcon,
|
||||||
: context.selected
|
context.active,
|
||||||
? st::dialogsSendingIconOver
|
context.selected);
|
||||||
: st::dialogsSendingIcon);
|
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}();
|
}();
|
||||||
|
@ -643,11 +634,10 @@ void PaintRow(
|
||||||
: context.selected
|
: context.selected
|
||||||
? &st::dialogsVerifiedIconOver
|
? &st::dialogsVerifiedIconOver
|
||||||
: &st::dialogsVerifiedIcon),
|
: &st::dialogsVerifiedIcon),
|
||||||
.premium = (context.active
|
.premium = &ThreeStateIcon(
|
||||||
? &st::dialogsPremiumIconActive
|
st::dialogsPremiumIcon,
|
||||||
: context.selected
|
context.active,
|
||||||
? &st::dialogsPremiumIconOver
|
context.selected),
|
||||||
: &st::dialogsPremiumIcon),
|
|
||||||
.scam = (context.active
|
.scam = (context.active
|
||||||
? &st::dialogsScamFgActive
|
? &st::dialogsScamFgActive
|
||||||
: context.selected
|
: context.selected
|
||||||
|
@ -710,30 +700,26 @@ const style::icon *ChatTypeIcon(
|
||||||
const PaintContext &context) {
|
const PaintContext &context) {
|
||||||
if (const auto user = peer->asUser()) {
|
if (const auto user = peer->asUser()) {
|
||||||
if (ShowUserBotIcon(user)) {
|
if (ShowUserBotIcon(user)) {
|
||||||
return &(context.active
|
return &ThreeStateIcon(
|
||||||
? st::dialogsBotIconActive
|
st::dialogsBotIcon,
|
||||||
: context.selected
|
context.active,
|
||||||
? st::dialogsBotIconOver
|
context.selected);
|
||||||
: st::dialogsBotIcon);
|
|
||||||
}
|
}
|
||||||
} else if (peer->isBroadcast()) {
|
} else if (peer->isBroadcast()) {
|
||||||
return &(context.active
|
return &ThreeStateIcon(
|
||||||
? st::dialogsChannelIconActive
|
st::dialogsChannelIcon,
|
||||||
: context.selected
|
context.active,
|
||||||
? st::dialogsChannelIconOver
|
context.selected);
|
||||||
: st::dialogsChannelIcon);
|
|
||||||
} else if (peer->isForum()) {
|
} else if (peer->isForum()) {
|
||||||
return &(context.active
|
return &ThreeStateIcon(
|
||||||
? st::dialogsForumIconActive
|
st::dialogsForumIcon,
|
||||||
: context.selected
|
context.active,
|
||||||
? st::dialogsForumIconOver
|
context.selected);
|
||||||
: st::dialogsForumIcon);
|
|
||||||
} else {
|
} else {
|
||||||
return &(context.active
|
return &ThreeStateIcon(
|
||||||
? st::dialogsChatIconActive
|
st::dialogsChatIcon,
|
||||||
: context.selected
|
context.active,
|
||||||
? st::dialogsChatIconOver
|
context.selected);
|
||||||
: st::dialogsChatIcon);
|
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history_item.h"
|
#include "history/history_item.h"
|
||||||
#include "history/view/history_view_item_preview.h"
|
#include "history/view/history_view_item_preview.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
#include "dialogs/dialogs_three_state_icon.h"
|
||||||
#include "dialogs/ui/dialogs_layout.h"
|
#include "dialogs/ui/dialogs_layout.h"
|
||||||
#include "dialogs/ui/dialogs_topics_view.h"
|
#include "dialogs/ui/dialogs_topics_view.h"
|
||||||
#include "ui/effects/spoiler_mess.h"
|
#include "ui/effects/spoiler_mess.h"
|
||||||
#include "ui/text/text_options.h"
|
#include "ui/text/text_options.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/image/image.h"
|
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/power_saving.h"
|
#include "ui/power_saving.h"
|
||||||
#include "core/ui_integration.h"
|
#include "core/ui_integration.h"
|
||||||
|
@ -159,6 +159,11 @@ void MessageView::prepare(
|
||||||
options.ignoreTopic = true;
|
options.ignoreTopic = true;
|
||||||
options.spoilerLoginCode = true;
|
options.spoilerLoginCode = true;
|
||||||
auto preview = item->toPreview(options);
|
auto preview = item->toPreview(options);
|
||||||
|
_leftIcon = (preview.icon == ItemPreview::Icon::ForwardedMessage)
|
||||||
|
? &st::dialogsMiniForwardIcon
|
||||||
|
: (preview.icon == ItemPreview::Icon::ReplyToStory)
|
||||||
|
? &st::dialogsMiniReplyStoryIcon
|
||||||
|
: nullptr;
|
||||||
const auto hasImages = !preview.images.empty();
|
const auto hasImages = !preview.images.empty();
|
||||||
const auto history = item->history();
|
const auto history = item->history();
|
||||||
const auto context = Core::MarkedTextContext{
|
const auto context = Core::MarkedTextContext{
|
||||||
|
@ -169,7 +174,7 @@ void MessageView::prepare(
|
||||||
const auto senderTill = (preview.arrowInTextPosition > 0)
|
const auto senderTill = (preview.arrowInTextPosition > 0)
|
||||||
? preview.arrowInTextPosition
|
? preview.arrowInTextPosition
|
||||||
: preview.imagesInTextPosition;
|
: preview.imagesInTextPosition;
|
||||||
if (hasImages && senderTill > 0) {
|
if ((hasImages || _leftIcon) && senderTill > 0) {
|
||||||
auto sender = Text::Mid(preview.text, 0, senderTill);
|
auto sender = Text::Mid(preview.text, 0, senderTill);
|
||||||
TextUtilities::Trim(sender);
|
TextUtilities::Trim(sender);
|
||||||
_senderCache.setMarkedText(
|
_senderCache.setMarkedText(
|
||||||
|
@ -314,6 +319,15 @@ void MessageView::paint(
|
||||||
rect.setLeft(rect.x() + skip);
|
rect.setLeft(rect.x() + skip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_leftIcon) {
|
||||||
|
const auto &icon = ThreeStateIcon(
|
||||||
|
*_leftIcon,
|
||||||
|
context.active,
|
||||||
|
context.selected);
|
||||||
|
icon.paint(p, rect.topLeft(), rect.width());
|
||||||
|
rect.setLeft(rect.x() + icon.width() + st::dialogsMiniIconSkip);
|
||||||
|
}
|
||||||
for (const auto &image : _imagesCache) {
|
for (const auto &image : _imagesCache) {
|
||||||
if (rect.width() < st::dialogsMiniPreview) {
|
if (rect.width() < st::dialogsMiniPreview) {
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -15,6 +15,7 @@ enum class ImageRoundRadius;
|
||||||
|
|
||||||
namespace style {
|
namespace style {
|
||||||
struct DialogRow;
|
struct DialogRow;
|
||||||
|
struct ThreeStateIcon;
|
||||||
} // namespace style
|
} // namespace style
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
@ -92,6 +93,7 @@ private:
|
||||||
mutable std::vector<ItemPreviewImage> _imagesCache;
|
mutable std::vector<ItemPreviewImage> _imagesCache;
|
||||||
mutable std::unique_ptr<SpoilerAnimation> _spoiler;
|
mutable std::unique_ptr<SpoilerAnimation> _spoiler;
|
||||||
mutable std::unique_ptr<LoadingContext> _loadingContext;
|
mutable std::unique_ptr<LoadingContext> _loadingContext;
|
||||||
|
mutable const style::ThreeStateIcon *_leftIcon = nullptr;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,19 +10,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/algorithm.h"
|
#include "base/algorithm.h"
|
||||||
#include "logs.h"
|
#include "logs.h"
|
||||||
|
|
||||||
|
#if !defined DESKTOP_APP_USE_PACKAGED && !defined Q_OS_WIN && !defined Q_OS_MAC
|
||||||
|
#include "base/platform/linux/base_linux_library.h"
|
||||||
|
#include <deque>
|
||||||
|
#endif // !DESKTOP_APP_USE_PACKAGED && !Q_OS_WIN && !Q_OS_MAC
|
||||||
|
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
|
|
||||||
#ifdef LIB_FFMPEG_USE_QT_PRIVATE_API
|
#ifdef LIB_FFMPEG_USE_QT_PRIVATE_API
|
||||||
#include <private/qdrawhelper_p.h>
|
#include <private/qdrawhelper_p.h>
|
||||||
#endif // LIB_FFMPEG_USE_QT_PRIVATE_API
|
#endif // LIB_FFMPEG_USE_QT_PRIVATE_API
|
||||||
|
|
||||||
#include <deque>
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <libavutil/opt.h>
|
#include <libavutil/opt.h>
|
||||||
#if !defined DESKTOP_APP_USE_PACKAGED && !defined Q_OS_WIN && !defined Q_OS_MAC
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#endif // !DESKTOP_APP_USE_PACKAGED && !Q_OS_WIN && !Q_OS_MAC
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
||||||
namespace FFmpeg {
|
namespace FFmpeg {
|
||||||
|
@ -95,19 +95,10 @@ void PremultiplyLine(uchar *dst, const uchar *src, int intsCount) {
|
||||||
auto list = std::deque{
|
auto list = std::deque{
|
||||||
AV_PIX_FMT_CUDA,
|
AV_PIX_FMT_CUDA,
|
||||||
};
|
};
|
||||||
const auto vdpau = [&] {
|
if (base::Platform::LoadLibrary("libvdpau.so.1")) {
|
||||||
if (const auto handle = dlopen("libvdpau.so.1", RTLD_LAZY)) {
|
|
||||||
dlclose(handle);
|
|
||||||
}
|
|
||||||
if (dlerror()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}();
|
|
||||||
if (vdpau) {
|
|
||||||
list.push_front(AV_PIX_FMT_VDPAU);
|
list.push_front(AV_PIX_FMT_VDPAU);
|
||||||
}
|
}
|
||||||
const auto va = [&] {
|
if ([&] {
|
||||||
const auto list = std::array{
|
const auto list = std::array{
|
||||||
"libva-drm.so.1",
|
"libva-drm.so.1",
|
||||||
"libva-x11.so.1",
|
"libva-x11.so.1",
|
||||||
|
@ -115,16 +106,12 @@ void PremultiplyLine(uchar *dst, const uchar *src, int intsCount) {
|
||||||
"libdrm.so.2",
|
"libdrm.so.2",
|
||||||
};
|
};
|
||||||
for (const auto lib : list) {
|
for (const auto lib : list) {
|
||||||
if (const auto handle = dlopen(lib, RTLD_LAZY)) {
|
if (!base::Platform::LoadLibrary(lib)) {
|
||||||
dlclose(handle);
|
|
||||||
}
|
|
||||||
if (dlerror()) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}();
|
}()) {
|
||||||
if (va) {
|
|
||||||
list.push_front(AV_PIX_FMT_VAAPI);
|
list.push_front(AV_PIX_FMT_VAAPI);
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
|
|
|
@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history_inner_widget.h"
|
#include "history/history_inner_widget.h"
|
||||||
|
|
||||||
#include "core/file_utilities.h"
|
#include "core/file_utilities.h"
|
||||||
#include "core/crash_reports.h"
|
|
||||||
#include "core/click_handler_types.h"
|
#include "core/click_handler_types.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/admin_log/history_admin_log_item.h"
|
#include "history/admin_log/history_admin_log_item.h"
|
||||||
|
@ -32,7 +31,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/menu/menu_add_action_callback_factory.h"
|
#include "ui/widgets/menu/menu_add_action_callback_factory.h"
|
||||||
#include "ui/widgets/menu/menu_multiline_action.h"
|
#include "ui/widgets/menu/menu_multiline_action.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "ui/image/image.h"
|
|
||||||
#include "ui/effects/path_shift_gradient.h"
|
#include "ui/effects/path_shift_gradient.h"
|
||||||
#include "ui/effects/message_sending_animation_controller.h"
|
#include "ui/effects/message_sending_animation_controller.h"
|
||||||
#include "ui/effects/reaction_fly_animation.h"
|
#include "ui/effects/reaction_fly_animation.h"
|
||||||
|
@ -40,16 +38,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/boxes/report_box.h"
|
#include "ui/boxes/report_box.h"
|
||||||
#include "ui/layers/generic_box.h"
|
#include "ui/layers/generic_box.h"
|
||||||
#include "ui/controls/delete_message_context_action.h"
|
#include "ui/controls/delete_message_context_action.h"
|
||||||
#include "ui/controls/who_reacted_context_action.h"
|
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "ui/ui_utility.h"
|
#include "ui/ui_utility.h"
|
||||||
#include "ui/cached_round_corners.h"
|
|
||||||
#include "ui/inactive_press.h"
|
#include "ui/inactive_press.h"
|
||||||
#include "window/window_adaptive.h"
|
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
#include "window/window_peer_menu.h"
|
#include "window/window_peer_menu.h"
|
||||||
#include "window/window_controller.h"
|
|
||||||
#include "window/notifications_manager.h"
|
#include "window/notifications_manager.h"
|
||||||
#include "boxes/about_sponsored_box.h"
|
#include "boxes/about_sponsored_box.h"
|
||||||
#include "boxes/delete_messages_box.h"
|
#include "boxes/delete_messages_box.h"
|
||||||
|
@ -94,12 +88,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_file_origin.h"
|
#include "data/data_file_origin.h"
|
||||||
#include "data/data_histories.h"
|
#include "data/data_histories.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
#include "data/stickers/data_stickers.h"
|
|
||||||
#include "data/data_sponsored_messages.h"
|
#include "data/data_sponsored_messages.h"
|
||||||
#include "dialogs/ui/dialogs_video_userpic.h"
|
#include "dialogs/ui/dialogs_video_userpic.h"
|
||||||
#include "settings/settings_premium.h"
|
#include "settings/settings_premium.h"
|
||||||
#include "styles/style_chat.h"
|
#include "styles/style_chat.h"
|
||||||
#include "styles/style_window.h" // st::windowMinWidth
|
|
||||||
#include "styles/style_menu_icons.h"
|
#include "styles/style_menu_icons.h"
|
||||||
|
|
||||||
#include <QtGui/QClipboard>
|
#include <QtGui/QClipboard>
|
||||||
|
|
|
@ -15,17 +15,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/history_view_message.h"
|
#include "history/view/history_view_message.h"
|
||||||
#include "history/view/history_view_service_message.h"
|
#include "history/view/history_view_service_message.h"
|
||||||
#include "history/view/media/history_view_media_grouped.h"
|
#include "history/view/media/history_view_media_grouped.h"
|
||||||
#include "history/history_item.h"
|
|
||||||
#include "history/history_item_components.h"
|
#include "history/history_item_components.h"
|
||||||
#include "history/history_item_helpers.h"
|
#include "history/history_item_helpers.h"
|
||||||
#include "history/history_unread_things.h"
|
#include "history/history_unread_things.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "mtproto/mtproto_config.h"
|
#include "mtproto/mtproto_config.h"
|
||||||
#include "media/clip/media_clip_reader.h"
|
#include "media/clip/media_clip_reader.h"
|
||||||
#include "ui/effects/ripple_animation.h"
|
|
||||||
#include "ui/text/format_values.h"
|
#include "ui/text/format_values.h"
|
||||||
#include "ui/text/text_isolated_emoji.h"
|
#include "ui/text/text_isolated_emoji.h"
|
||||||
#include "ui/text/text_options.h"
|
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "storage/file_upload.h"
|
#include "storage/file_upload.h"
|
||||||
#include "storage/storage_facade.h"
|
#include "storage/storage_facade.h"
|
||||||
|
@ -41,7 +38,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "core/crash_reports.h"
|
|
||||||
#include "core/click_handler_types.h"
|
#include "core/click_handler_types.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
#include "base/timer_rpl.h"
|
#include "base/timer_rpl.h"
|
||||||
|
@ -72,7 +68,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "chat_helpers/stickers_gift_box_pack.h"
|
#include "chat_helpers/stickers_gift_box_pack.h"
|
||||||
#include "payments/payments_checkout_process.h" // CheckoutProcess::Start.
|
#include "payments/payments_checkout_process.h" // CheckoutProcess::Start.
|
||||||
#include "styles/style_dialogs.h"
|
#include "styles/style_dialogs.h"
|
||||||
#include "styles/style_chat.h"
|
|
||||||
|
|
||||||
#include "ayu/ayu_settings.h"
|
#include "ayu/ayu_settings.h"
|
||||||
|
|
||||||
|
@ -2972,6 +2967,11 @@ ItemPreview HistoryItem::toPreview(ToPreviewOptions options) const {
|
||||||
? tr::lng_from_you(tr::now)
|
? tr::lng_from_you(tr::now)
|
||||||
: sender->shortName();
|
: sender->shortName();
|
||||||
};
|
};
|
||||||
|
result.icon = (Get<HistoryMessageForwarded>() != nullptr)
|
||||||
|
? ItemPreview::Icon::ForwardedMessage
|
||||||
|
: replyToStory().valid()
|
||||||
|
? ItemPreview::Icon::ReplyToStory
|
||||||
|
: ItemPreview::Icon::None;
|
||||||
const auto fromForwarded = [&]() -> std::optional<QString> {
|
const auto fromForwarded = [&]() -> std::optional<QString> {
|
||||||
if (const auto forwarded = Get<HistoryMessageForwarded>()) {
|
if (const auto forwarded = Get<HistoryMessageForwarded>()) {
|
||||||
return forwarded->originalSender
|
return forwarded->originalSender
|
||||||
|
|
|
@ -43,6 +43,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "api/api_bot.h"
|
#include "api/api_bot.h"
|
||||||
#include "styles/style_widgets.h"
|
#include "styles/style_widgets.h"
|
||||||
#include "styles/style_chat.h"
|
#include "styles/style_chat.h"
|
||||||
|
#include "styles/style_dialogs.h" // dialogsMiniReplyStoryIcon.
|
||||||
|
|
||||||
#include <QtGui/QGuiApplication>
|
#include <QtGui/QGuiApplication>
|
||||||
|
|
||||||
|
@ -460,7 +461,14 @@ void HistoryMessageReply::updateName(
|
||||||
w += st::msgServiceFont->spacew + replyToVia->maxWidth;
|
w += st::msgServiceFont->spacew + replyToVia->maxWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
maxReplyWidth = previewSkip + qMax(w, qMin(replyToText.maxWidth(), int32(st::maxSignatureSize)));
|
maxReplyWidth = previewSkip
|
||||||
|
+ std::max(
|
||||||
|
w,
|
||||||
|
std::min(replyToText.maxWidth(), st::maxSignatureSize))
|
||||||
|
+ (storyReply
|
||||||
|
? (st::dialogsMiniIconSkip
|
||||||
|
+ st::dialogsMiniReplyStoryIcon.icon.width())
|
||||||
|
: 0);
|
||||||
} else {
|
} else {
|
||||||
maxReplyWidth = st::msgDateFont->width(statePhrase());
|
maxReplyWidth = st::msgDateFont->width(statePhrase());
|
||||||
}
|
}
|
||||||
|
@ -596,14 +604,27 @@ void HistoryMessageReply::paint(
|
||||||
? stm->historyTextFg
|
? stm->historyTextFg
|
||||||
: st->msgImgReplyBarColor());
|
: st->msgImgReplyBarColor());
|
||||||
holder->prepareCustomEmojiPaint(p, context, replyToText);
|
holder->prepareCustomEmojiPaint(p, context, replyToText);
|
||||||
|
auto replyToTextPosition = QPoint(
|
||||||
|
x + st::msgReplyBarSkip + previewSkip,
|
||||||
|
y + st::msgReplyPadding.top() + st::msgServiceNameFont->height);
|
||||||
|
const auto replyToTextPalette = &(inBubble
|
||||||
|
? stm->replyTextPalette
|
||||||
|
: st->imgReplyTextPalette());
|
||||||
|
if (storyReply) {
|
||||||
|
st::dialogsMiniReplyStoryIcon.icon.paint(
|
||||||
|
p,
|
||||||
|
replyToTextPosition,
|
||||||
|
w - st::msgReplyBarSkip - previewSkip,
|
||||||
|
replyToTextPalette->linkFg->c);
|
||||||
|
replyToTextPosition += QPoint(
|
||||||
|
st::dialogsMiniIconSkip
|
||||||
|
+ st::dialogsMiniReplyStoryIcon.icon.width(),
|
||||||
|
0);
|
||||||
|
}
|
||||||
replyToText.draw(p, {
|
replyToText.draw(p, {
|
||||||
.position = QPoint(
|
.position = replyToTextPosition,
|
||||||
x + st::msgReplyBarSkip + previewSkip,
|
|
||||||
y + st::msgReplyPadding.top() + st::msgServiceNameFont->height),
|
|
||||||
.availableWidth = w - st::msgReplyBarSkip - previewSkip,
|
.availableWidth = w - st::msgReplyBarSkip - previewSkip,
|
||||||
.palette = &(inBubble
|
.palette = replyToTextPalette,
|
||||||
? stm->replyTextPalette
|
|
||||||
: st->imgReplyTextPalette()),
|
|
||||||
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
||||||
.now = context.now,
|
.now = context.now,
|
||||||
.pausedEmoji = (context.paused
|
.pausedEmoji = (context.paused
|
||||||
|
|
|
@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "api/api_chat_participants.h"
|
#include "api/api_chat_participants.h"
|
||||||
#include "api/api_report.h"
|
#include "api/api_report.h"
|
||||||
#include "api/api_sending.h"
|
#include "api/api_sending.h"
|
||||||
#include "api/api_text_entities.h"
|
|
||||||
#include "api/api_send_progress.h"
|
#include "api/api_send_progress.h"
|
||||||
#include "api/api_unread_things.h"
|
#include "api/api_unread_things.h"
|
||||||
#include "ui/boxes/confirm_box.h"
|
#include "ui/boxes/confirm_box.h"
|
||||||
|
@ -32,12 +31,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/inner_dropdown.h"
|
#include "ui/widgets/inner_dropdown.h"
|
||||||
#include "ui/widgets/dropdown_menu.h"
|
#include "ui/widgets/dropdown_menu.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
#include "ui/widgets/shadow.h"
|
|
||||||
#include "ui/effects/ripple_animation.h"
|
#include "ui/effects/ripple_animation.h"
|
||||||
#include "ui/effects/message_sending_animation_controller.h"
|
#include "ui/effects/message_sending_animation_controller.h"
|
||||||
#include "ui/text/text_utilities.h" // Ui::Text::ToUpper
|
#include "ui/text/text_utilities.h" // Ui::Text::ToUpper
|
||||||
#include "ui/text/format_values.h"
|
#include "ui/text/format_values.h"
|
||||||
#include "ui/chat/forward_options_box.h"
|
|
||||||
#include "ui/chat/message_bar.h"
|
#include "ui/chat/message_bar.h"
|
||||||
#include "ui/chat/attach/attach_send_files_way.h"
|
#include "ui/chat/attach/attach_send_files_way.h"
|
||||||
#include "ui/chat/choose_send_as.h"
|
#include "ui/chat/choose_send_as.h"
|
||||||
|
@ -62,7 +59,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
#include "data/data_photo.h"
|
#include "data/data_photo.h"
|
||||||
#include "data/data_photo_media.h"
|
#include "data/data_photo_media.h"
|
||||||
#include "data/data_media_types.h"
|
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
#include "data/data_chat.h"
|
#include "data/data_chat.h"
|
||||||
#include "data/data_forum.h"
|
#include "data/data_forum.h"
|
||||||
|
@ -107,7 +103,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/history_view_translate_bar.h"
|
#include "history/view/history_view_translate_bar.h"
|
||||||
#include "history/view/media/history_view_media.h"
|
#include "history/view/media/history_view_media.h"
|
||||||
#include "profile/profile_block_group_members.h"
|
#include "profile/profile_block_group_members.h"
|
||||||
#include "info/info_memento.h"
|
|
||||||
#include "core/click_handler_types.h"
|
#include "core/click_handler_types.h"
|
||||||
#include "chat_helpers/tabbed_panel.h"
|
#include "chat_helpers/tabbed_panel.h"
|
||||||
#include "chat_helpers/tabbed_selector.h"
|
#include "chat_helpers/tabbed_selector.h"
|
||||||
|
@ -138,7 +133,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/chat/continuous_scroll.h"
|
#include "ui/chat/continuous_scroll.h"
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "ui/item_text_options.h"
|
#include "ui/item_text_options.h"
|
||||||
#include "ui/unread_badge.h"
|
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "main/main_session_settings.h"
|
#include "main/main_session_settings.h"
|
||||||
#include "main/session/send_as_peers.h"
|
#include "main/session/send_as_peers.h"
|
||||||
|
@ -152,7 +146,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "inline_bots/bot_attach_web_view.h"
|
#include "inline_bots/bot_attach_web_view.h"
|
||||||
#include "info/profile/info_profile_values.h" // SharedMediaCountValue.
|
#include "info/profile/info_profile_values.h" // SharedMediaCountValue.
|
||||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||||
#include "core/crash_reports.h"
|
|
||||||
#include "core/shortcuts.h"
|
#include "core/shortcuts.h"
|
||||||
#include "core/ui_integration.h"
|
#include "core/ui_integration.h"
|
||||||
#include "support/support_common.h"
|
#include "support/support_common.h"
|
||||||
|
@ -160,12 +153,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "support/support_preload.h"
|
#include "support/support_preload.h"
|
||||||
#include "dialogs/dialogs_key.h"
|
#include "dialogs/dialogs_key.h"
|
||||||
#include "calls/calls_instance.h"
|
#include "calls/calls_instance.h"
|
||||||
#include "api/api_bot.h"
|
|
||||||
#include "styles/style_chat.h"
|
#include "styles/style_chat.h"
|
||||||
#include "styles/style_dialogs.h"
|
#include "styles/style_dialogs.h"
|
||||||
#include "styles/style_window.h"
|
#include "styles/style_window.h"
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
#include "styles/style_profile.h"
|
|
||||||
#include "styles/style_chat_helpers.h"
|
#include "styles/style_chat_helpers.h"
|
||||||
#include "styles/style_info.h"
|
#include "styles/style_info.h"
|
||||||
|
|
||||||
|
|
|
@ -23,11 +23,17 @@ struct ItemPreviewImage {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ItemPreview {
|
struct ItemPreview {
|
||||||
|
enum class Icon {
|
||||||
|
None,
|
||||||
|
ForwardedMessage,
|
||||||
|
ReplyToStory,
|
||||||
|
};
|
||||||
TextWithEntities text;
|
TextWithEntities text;
|
||||||
std::vector<ItemPreviewImage> images;
|
std::vector<ItemPreviewImage> images;
|
||||||
int arrowInTextPosition = -1;
|
int arrowInTextPosition = -1;
|
||||||
int imagesInTextPosition = 0;
|
int imagesInTextPosition = 0;
|
||||||
std::any loadingContext;
|
std::any loadingContext;
|
||||||
|
Icon icon = Icon::None;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ToPreviewOptions {
|
struct ToPreviewOptions {
|
||||||
|
|
|
@ -759,7 +759,9 @@ QSize Message::performCountOptimalSize() {
|
||||||
: item->hiddenSenderInfo()->nameText();
|
: item->hiddenSenderInfo()->nameText();
|
||||||
auto namew = st::msgPadding.left()
|
auto namew = st::msgPadding.left()
|
||||||
+ name.maxWidth()
|
+ name.maxWidth()
|
||||||
+ (_fromNameStatus ? st::dialogsPremiumIcon.width() : 0)
|
+ (_fromNameStatus
|
||||||
|
? st::dialogsPremiumIcon.icon.width()
|
||||||
|
: 0)
|
||||||
+ st::msgPadding.right();
|
+ st::msgPadding.right();
|
||||||
if (via && !displayForwardedFrom()) {
|
if (via && !displayForwardedFrom()) {
|
||||||
namew += st::msgServiceFont->spacew + via->maxWidth
|
namew += st::msgServiceFont->spacew + via->maxWidth
|
||||||
|
@ -1358,7 +1360,7 @@ void Message::paintFromName(
|
||||||
return &info->nameText();
|
return &info->nameText();
|
||||||
}();
|
}();
|
||||||
const auto statusWidth = _fromNameStatus
|
const auto statusWidth = _fromNameStatus
|
||||||
? st::dialogsPremiumIcon.width()
|
? st::dialogsPremiumIcon.icon.width()
|
||||||
: 0;
|
: 0;
|
||||||
if (statusWidth && availableWidth > statusWidth) {
|
if (statusWidth && availableWidth > statusWidth) {
|
||||||
const auto x = availableLeft
|
const auto x = availableLeft
|
||||||
|
@ -1398,7 +1400,7 @@ void Message::paintFromName(
|
||||||
.paused = context.paused || On(PowerSaving::kEmojiStatus),
|
.paused = context.paused || On(PowerSaving::kEmojiStatus),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
st::dialogsPremiumIcon.paint(p, x, y, width(), color);
|
st::dialogsPremiumIcon.icon.paint(p, x, y, width(), color);
|
||||||
}
|
}
|
||||||
availableWidth -= statusWidth;
|
availableWidth -= statusWidth;
|
||||||
}
|
}
|
||||||
|
@ -1407,7 +1409,8 @@ void Message::paintFromName(
|
||||||
nameText->drawElided(p, availableLeft, trect.top(), availableWidth);
|
nameText->drawElided(p, availableLeft, trect.top(), availableWidth);
|
||||||
const auto skipWidth = nameText->maxWidth()
|
const auto skipWidth = nameText->maxWidth()
|
||||||
+ (_fromNameStatus
|
+ (_fromNameStatus
|
||||||
? (st::dialogsPremiumIcon.width() + st::msgServiceFont->spacew)
|
? (st::dialogsPremiumIcon.icon.width()
|
||||||
|
+ st::msgServiceFont->spacew)
|
||||||
: 0)
|
: 0)
|
||||||
+ st::msgServiceFont->spacew;
|
+ st::msgServiceFont->spacew;
|
||||||
availableLeft += skipWidth;
|
availableLeft += skipWidth;
|
||||||
|
@ -3525,7 +3528,7 @@ void Message::fromNameUpdated(int width) const {
|
||||||
- st::msgPadding.right()
|
- st::msgPadding.right()
|
||||||
- nameText->maxWidth()
|
- nameText->maxWidth()
|
||||||
+ (_fromNameStatus
|
+ (_fromNameStatus
|
||||||
? (st::dialogsPremiumIcon.width()
|
? (st::dialogsPremiumIcon.icon.width()
|
||||||
+ st::msgServiceFont->spacew)
|
+ st::msgServiceFont->spacew)
|
||||||
: 0)
|
: 0)
|
||||||
- st::msgServiceFont->spacew);
|
- st::msgServiceFont->spacew);
|
||||||
|
|
|
@ -566,7 +566,7 @@ void TopBarWidget::paintTopBar(Painter &p) {
|
||||||
{
|
{
|
||||||
.peer = peer,
|
.peer = peer,
|
||||||
.verified = &st::dialogsVerifiedIcon,
|
.verified = &st::dialogsVerifiedIcon,
|
||||||
.premium = &st::dialogsPremiumIcon,
|
.premium = &st::dialogsPremiumIcon.icon,
|
||||||
.scam = &st::attentionButtonFg,
|
.scam = &st::attentionButtonFg,
|
||||||
.premiumFg = &st::dialogsVerifiedIconBg,
|
.premiumFg = &st::dialogsVerifiedIconBg,
|
||||||
.customEmojiRepaint = [=] { update(); },
|
.customEmojiRepaint = [=] { update(); },
|
||||||
|
|
|
@ -241,7 +241,7 @@ int Selector::countWidth(int desiredWidth, int maxWidth) {
|
||||||
return std::max(2 * _skipx + _columns * _size, desiredWidth);
|
return std::max(2 * _skipx + _columns * _size, desiredWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
QMargins Selector::extentsForShadow() const {
|
QMargins Selector::marginsForShadow() const {
|
||||||
const auto line = st::lineWidth;
|
const auto line = st::lineWidth;
|
||||||
return useTransparency()
|
return useTransparency()
|
||||||
? st::reactionCornerShadow
|
? st::reactionCornerShadow
|
||||||
|
@ -264,26 +264,26 @@ void Selector::setSpecialExpandTopSkip(int skip) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Selector::initGeometry(int innerTop) {
|
void Selector::initGeometry(int innerTop) {
|
||||||
const auto extents = extentsForShadow();
|
const auto margins = marginsForShadow();
|
||||||
const auto parent = parentWidget()->rect();
|
const auto parent = parentWidget()->rect();
|
||||||
const auto innerWidth = 2 * _skipx + _columns * _size;
|
const auto innerWidth = 2 * _skipx + _columns * _size;
|
||||||
const auto innerHeight = st::reactStripHeight;
|
const auto innerHeight = st::reactStripHeight;
|
||||||
const auto width = _useTransparency
|
const auto width = _useTransparency
|
||||||
? (innerWidth + extents.left() + extents.right())
|
? (innerWidth + margins.left() + margins.right())
|
||||||
: parent.width();
|
: parent.width();
|
||||||
const auto height = innerHeight + extents.top() + extents.bottom();
|
const auto height = innerHeight + margins.top() + margins.bottom();
|
||||||
const auto left = style::RightToLeft() ? 0 : (parent.width() - width);
|
const auto left = style::RightToLeft() ? 0 : (parent.width() - width);
|
||||||
_collapsedTopSkip = _useTransparency
|
_collapsedTopSkip = _useTransparency
|
||||||
? (extendTopForCategories() + _specialExpandTopSkip)
|
? (extendTopForCategories() + _specialExpandTopSkip)
|
||||||
: 0;
|
: 0;
|
||||||
const auto top = innerTop - extents.top() - _collapsedTopSkip;
|
const auto top = innerTop - margins.top() - _collapsedTopSkip;
|
||||||
const auto add = _st.icons.stripBubble.height() - extents.bottom();
|
const auto add = _st.icons.stripBubble.height() - margins.bottom();
|
||||||
_outer = QRect(0, _collapsedTopSkip, width, height);
|
_outer = QRect(0, _collapsedTopSkip, width, height);
|
||||||
_outerWithBubble = _outer.marginsAdded({ 0, 0, 0, add });
|
_outerWithBubble = _outer.marginsAdded({ 0, 0, 0, add });
|
||||||
setGeometry(_outerWithBubble.marginsAdded(
|
setGeometry(_outerWithBubble.marginsAdded(
|
||||||
{ 0, _collapsedTopSkip, 0, 0 }
|
{ 0, _collapsedTopSkip, 0, 0 }
|
||||||
).translated(left, top));
|
).translated(left, top));
|
||||||
_inner = _outer.marginsRemoved(extents);
|
_inner = _outer.marginsRemoved(margins);
|
||||||
|
|
||||||
if (!_strip) {
|
if (!_strip) {
|
||||||
expand();
|
expand();
|
||||||
|
@ -343,9 +343,9 @@ void Selector::paintAppearing(QPainter &p) {
|
||||||
}
|
}
|
||||||
_paintBuffer.fill(_st.bg->c);
|
_paintBuffer.fill(_st.bg->c);
|
||||||
auto q = QPainter(&_paintBuffer);
|
auto q = QPainter(&_paintBuffer);
|
||||||
const auto extents = extentsForShadow();
|
const auto margins = marginsForShadow();
|
||||||
const auto appearedWidth = countAppearedWidth(_appearProgress);
|
const auto appearedWidth = countAppearedWidth(_appearProgress);
|
||||||
const auto fullWidth = _inner.x() + appearedWidth + extents.right();
|
const auto fullWidth = _inner.x() + appearedWidth + margins.right();
|
||||||
const auto size = QSize(fullWidth, _outer.height());
|
const auto size = QSize(fullWidth, _outer.height());
|
||||||
|
|
||||||
q.translate(_inner.topLeft() - QPoint(0, _collapsedTopSkip));
|
q.translate(_inner.topLeft() - QPoint(0, _collapsedTopSkip));
|
||||||
|
@ -455,7 +455,7 @@ auto Selector::paintExpandingBg(QPainter &p, float64 progress)
|
||||||
const auto radius = _reactions.customAllowed
|
const auto radius = _reactions.customAllowed
|
||||||
? (radiusStart + progress * (radiusEnd - radiusStart))
|
? (radiusStart + progress * (radiusEnd - radiusStart))
|
||||||
: radiusStart;
|
: radiusStart;
|
||||||
const auto extents = extentsForShadow();
|
const auto margins = marginsForShadow();
|
||||||
const auto expanding = anim::easeOutCirc(1., progress);
|
const auto expanding = anim::easeOutCirc(1., progress);
|
||||||
const auto expandUp = anim::interpolate(0, _collapsedTopSkip, expanding);
|
const auto expandUp = anim::interpolate(0, _collapsedTopSkip, expanding);
|
||||||
const auto expandDown = anim::interpolate(
|
const auto expandDown = anim::interpolate(
|
||||||
|
@ -470,7 +470,7 @@ auto Selector::paintExpandingBg(QPainter &p, float64 progress)
|
||||||
p.fillRect(fill, _st.bg);
|
p.fillRect(fill, _st.bg);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const auto inner = outer.marginsRemoved(extentsForShadow());
|
const auto inner = outer.marginsRemoved(marginsForShadow());
|
||||||
p.fillRect(inner, _st.bg);
|
p.fillRect(inner, _st.bg);
|
||||||
p.fillRect(
|
p.fillRect(
|
||||||
inner.x(),
|
inner.x(),
|
||||||
|
@ -483,7 +483,7 @@ auto Selector::paintExpandingBg(QPainter &p, float64 progress)
|
||||||
0,
|
0,
|
||||||
extendTopForCategories(),
|
extendTopForCategories(),
|
||||||
expanding);
|
expanding);
|
||||||
const auto inner = outer.marginsRemoved(extents);
|
const auto inner = outer.marginsRemoved(margins);
|
||||||
_shadowTop = inner.y() + categories;
|
_shadowTop = inner.y() + categories;
|
||||||
_shadowSkip = (_useTransparency && categories < radius)
|
_shadowSkip = (_useTransparency && categories < radius)
|
||||||
? int(base::SafeRound(
|
? int(base::SafeRound(
|
||||||
|
@ -494,7 +494,7 @@ auto Selector::paintExpandingBg(QPainter &p, float64 progress)
|
||||||
.list = inner.marginsRemoved({ 0, categories, 0, 0 }),
|
.list = inner.marginsRemoved({ 0, categories, 0, 0 }),
|
||||||
.radius = radius,
|
.radius = radius,
|
||||||
.expanding = expanding,
|
.expanding = expanding,
|
||||||
.finalBottom = height() - extents.bottom(),
|
.finalBottom = height() - margins.bottom(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -521,7 +521,7 @@ void Selector::paintExpanded(QPainter &p) {
|
||||||
if (_useTransparency) {
|
if (_useTransparency) {
|
||||||
p.drawImage(0, 0, _paintBuffer);
|
p.drawImage(0, 0, _paintBuffer);
|
||||||
} else {
|
} else {
|
||||||
const auto inner = rect().marginsRemoved(extentsForShadow());
|
const auto inner = rect().marginsRemoved(marginsForShadow());
|
||||||
p.fillRect(inner, _st.bg);
|
p.fillRect(inner, _st.bg);
|
||||||
p.fillRect(
|
p.fillRect(
|
||||||
inner.x(),
|
inner.x(),
|
||||||
|
@ -694,13 +694,13 @@ void Selector::expand() {
|
||||||
_willExpand.fire({});
|
_willExpand.fire({});
|
||||||
preloadAllRecentsAnimations();
|
preloadAllRecentsAnimations();
|
||||||
const auto parent = parentWidget()->geometry();
|
const auto parent = parentWidget()->geometry();
|
||||||
const auto extents = extentsForShadow();
|
const auto margins = marginsForShadow();
|
||||||
const auto heightLimit = _reactions.customAllowed
|
const auto heightLimit = _reactions.customAllowed
|
||||||
? st::emojiPanMaxHeight
|
? st::emojiPanMaxHeight
|
||||||
: minimalHeight();
|
: minimalHeight();
|
||||||
const auto willBeHeight = std::min(
|
const auto willBeHeight = std::min(
|
||||||
parent.height() - y(),
|
parent.height() - y(),
|
||||||
extents.top() + heightLimit + extents.bottom());
|
margins.top() + heightLimit + margins.bottom());
|
||||||
const auto additionalBottom = willBeHeight - height();
|
const auto additionalBottom = willBeHeight - height();
|
||||||
const auto additional = _specialExpandTopSkip + additionalBottom;
|
const auto additional = _specialExpandTopSkip + additionalBottom;
|
||||||
if (additionalBottom < 0 || additional <= 0) {
|
if (additionalBottom < 0 || additional <= 0) {
|
||||||
|
@ -834,7 +834,7 @@ void Selector::createList() {
|
||||||
_list->jumpedToPremium(
|
_list->jumpedToPremium(
|
||||||
) | rpl::start_with_next(_jumpedToPremium, _list->lifetime());
|
) | rpl::start_with_next(_jumpedToPremium, _list->lifetime());
|
||||||
|
|
||||||
const auto inner = rect().marginsRemoved(extentsForShadow());
|
const auto inner = rect().marginsRemoved(marginsForShadow());
|
||||||
const auto footer = _reactions.customAllowed
|
const auto footer = _reactions.customAllowed
|
||||||
? _list->createFooter().data()
|
? _list->createFooter().data()
|
||||||
: nullptr;
|
: nullptr;
|
||||||
|
@ -904,16 +904,16 @@ bool AdjustMenuGeometryForSelector(
|
||||||
const auto desiredWidth = menu->menu()->width() + added;
|
const auto desiredWidth = menu->menu()->width() + added;
|
||||||
const auto maxWidth = menu->st().menu.widthMax + added;
|
const auto maxWidth = menu->st().menu.widthMax + added;
|
||||||
const auto width = selector->countWidth(desiredWidth, maxWidth);
|
const auto width = selector->countWidth(desiredWidth, maxWidth);
|
||||||
const auto extents = selector->extentsForShadow();
|
const auto margins = selector->marginsForShadow();
|
||||||
const auto categoriesTop = selector->useTransparency()
|
const auto categoriesTop = selector->useTransparency()
|
||||||
? selector->extendTopForCategories()
|
? selector->extendTopForCategories()
|
||||||
: 0;
|
: 0;
|
||||||
menu->setForceWidth(width - added);
|
menu->setForceWidth(width - added);
|
||||||
const auto height = menu->height();
|
const auto height = menu->height();
|
||||||
const auto fullTop = extents.top() + categoriesTop + extend.top();
|
const auto fullTop = margins.top() + categoriesTop + extend.top();
|
||||||
const auto minimalHeight = extents.top()
|
const auto minimalHeight = margins.top()
|
||||||
+ selector->minimalHeight()
|
+ selector->minimalHeight()
|
||||||
+ extents.bottom();
|
+ margins.bottom();
|
||||||
const auto willBeHeightWithoutBottomPadding = fullTop
|
const auto willBeHeightWithoutBottomPadding = fullTop
|
||||||
+ height
|
+ height
|
||||||
- menu->st().shadow.extend.top();
|
- menu->st().shadow.extend.top();
|
||||||
|
@ -924,15 +924,15 @@ bool AdjustMenuGeometryForSelector(
|
||||||
? (minimalHeight - willBeHeightWithoutBottomPadding)
|
? (minimalHeight - willBeHeightWithoutBottomPadding)
|
||||||
: 0);
|
: 0);
|
||||||
menu->setAdditionalMenuPadding(QMargins(
|
menu->setAdditionalMenuPadding(QMargins(
|
||||||
extents.left() + extend.left(),
|
margins.left() + extend.left(),
|
||||||
fullTop,
|
fullTop,
|
||||||
extents.right() + extend.right(),
|
margins.right() + extend.right(),
|
||||||
additionalPaddingBottom
|
additionalPaddingBottom
|
||||||
), QMargins(
|
), QMargins(
|
||||||
extents.left(),
|
margins.left(),
|
||||||
extents.top(),
|
margins.top(),
|
||||||
extents.right(),
|
margins.right(),
|
||||||
std::min(additionalPaddingBottom, extents.bottom())
|
std::min(additionalPaddingBottom, margins.bottom())
|
||||||
));
|
));
|
||||||
if (!menu->prepareGeometryFor(desiredPosition)) {
|
if (!menu->prepareGeometryFor(desiredPosition)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -944,14 +944,14 @@ bool AdjustMenuGeometryForSelector(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
menu->setAdditionalMenuPadding(QMargins(
|
menu->setAdditionalMenuPadding(QMargins(
|
||||||
extents.left() + extend.left(),
|
margins.left() + extend.left(),
|
||||||
fullTop + additionalPaddingBottom,
|
fullTop + additionalPaddingBottom,
|
||||||
extents.right() + extend.right(),
|
margins.right() + extend.right(),
|
||||||
0
|
0
|
||||||
), QMargins(
|
), QMargins(
|
||||||
extents.left(),
|
margins.left(),
|
||||||
extents.top(),
|
margins.top(),
|
||||||
extents.right(),
|
margins.right(),
|
||||||
0
|
0
|
||||||
));
|
));
|
||||||
selector->setSpecialExpandTopSkip(additionalPaddingBottom);
|
selector->setSpecialExpandTopSkip(additionalPaddingBottom);
|
||||||
|
|
|
@ -61,7 +61,7 @@ public:
|
||||||
[[nodiscard]] bool useTransparency() const;
|
[[nodiscard]] bool useTransparency() const;
|
||||||
|
|
||||||
int countWidth(int desiredWidth, int maxWidth);
|
int countWidth(int desiredWidth, int maxWidth);
|
||||||
[[nodiscard]] QMargins extentsForShadow() const;
|
[[nodiscard]] QMargins marginsForShadow() const;
|
||||||
[[nodiscard]] int extendTopForCategories() const;
|
[[nodiscard]] int extendTopForCategories() const;
|
||||||
[[nodiscard]] int minimalHeight() const;
|
[[nodiscard]] int minimalHeight() const;
|
||||||
[[nodiscard]] int countAppearedWidth(float64 progress) const;
|
[[nodiscard]] int countAppearedWidth(float64 progress) const;
|
||||||
|
|
|
@ -243,18 +243,18 @@ void Reactions::Panel::create() {
|
||||||
const auto desiredWidth = st::storiesReactionsWidth;
|
const auto desiredWidth = st::storiesReactionsWidth;
|
||||||
const auto maxWidth = desiredWidth * 2;
|
const auto maxWidth = desiredWidth * 2;
|
||||||
const auto width = _selector->countWidth(desiredWidth, maxWidth);
|
const auto width = _selector->countWidth(desiredWidth, maxWidth);
|
||||||
const auto extents = _selector->extentsForShadow();
|
const auto margins = _selector->marginsForShadow();
|
||||||
const auto categoriesTop = _selector->extendTopForCategories();
|
const auto categoriesTop = _selector->extendTopForCategories();
|
||||||
const auto full = extents.left() + width + extents.right();
|
const auto full = margins.left() + width + margins.right();
|
||||||
|
|
||||||
_shownValue = 0.;
|
_shownValue = 0.;
|
||||||
rpl::combine(
|
rpl::combine(
|
||||||
_controller->layoutValue(),
|
_controller->layoutValue(),
|
||||||
_shownValue.value()
|
_shownValue.value()
|
||||||
) | rpl::start_with_next([=](const Layout &layout, float64 shown) {
|
) | rpl::start_with_next([=](const Layout &layout, float64 shown) {
|
||||||
const auto width = extents.left()
|
const auto width = margins.left()
|
||||||
+ _selector->countAppearedWidth(shown)
|
+ _selector->countAppearedWidth(shown)
|
||||||
+ extents.right();
|
+ margins.right();
|
||||||
const auto height = layout.reactions.height();
|
const auto height = layout.reactions.height();
|
||||||
const auto shift = (width / 2);
|
const auto shift = (width / 2);
|
||||||
const auto right = (mode == Mode::Message)
|
const auto right = (mode == Mode::Message)
|
||||||
|
@ -271,7 +271,7 @@ void Reactions::Panel::create() {
|
||||||
const auto innerTop = height
|
const auto innerTop = height
|
||||||
- st::storiesReactionsBottomSkip
|
- st::storiesReactionsBottomSkip
|
||||||
- st::reactStripHeight;
|
- st::reactStripHeight;
|
||||||
const auto maxAdded = innerTop - extents.top() - categoriesTop;
|
const auto maxAdded = innerTop - margins.top() - categoriesTop;
|
||||||
const auto added = std::min(maxAdded, st::storiesReactionsAddedTop);
|
const auto added = std::min(maxAdded, st::storiesReactionsAddedTop);
|
||||||
_selector->setSpecialExpandTopSkip(added);
|
_selector->setSpecialExpandTopSkip(added);
|
||||||
_selector->initGeometry(innerTop);
|
_selector->initGeometry(innerTop);
|
||||||
|
|
|
@ -88,7 +88,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "main/main_session_settings.h"
|
#include "main/main_session_settings.h"
|
||||||
#include "layout/layout_document_generic_preview.h"
|
#include "layout/layout_document_generic_preview.h"
|
||||||
#include "platform/platform_overlay_widget.h"
|
#include "platform/platform_overlay_widget.h"
|
||||||
#include "settings/settings_premium.h"
|
|
||||||
#include "storage/file_download.h"
|
#include "storage/file_download.h"
|
||||||
#include "storage/storage_account.h"
|
#include "storage/storage_account.h"
|
||||||
#include "calls/calls_instance.h"
|
#include "calls/calls_instance.h"
|
||||||
|
@ -104,7 +103,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include <QtWidgets/QApplication>
|
#include <QtWidgets/QApplication>
|
||||||
#include <QtCore/QBuffer>
|
#include <QtCore/QBuffer>
|
||||||
#include <QtGui/QGuiApplication>
|
#include <QtGui/QGuiApplication>
|
||||||
#include <QtGui/QClipboard>
|
|
||||||
#include <QtGui/QWindow>
|
#include <QtGui/QWindow>
|
||||||
#include <QtGui/QScreen>
|
#include <QtGui/QScreen>
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_thread.h"
|
#include "data/data_thread.h"
|
||||||
#include "data/notify/data_notify_settings.h"
|
#include "data/notify/data_notify_settings.h"
|
||||||
|
#include "data/notify/data_peer_notify_settings.h"
|
||||||
#include "info/profile/info_profile_values.h"
|
#include "info/profile/info_profile_values.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
@ -31,10 +32,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "styles/style_menu_icons.h"
|
#include "styles/style_menu_icons.h"
|
||||||
|
|
||||||
namespace MuteMenu {
|
namespace MuteMenu {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kMuteDurSecondsDefault = crl::time(8) * 3600;
|
constexpr auto kMuteDurSecondsDefault = crl::time(8) * 3600;
|
||||||
|
constexpr auto kMuteForeverValue = std::numeric_limits<TimeId>::max();
|
||||||
|
|
||||||
class IconWithText final : public Ui::Menu::Action {
|
class IconWithText final : public Ui::Menu::Action {
|
||||||
public:
|
public:
|
||||||
|
@ -70,7 +71,7 @@ public:
|
||||||
MuteItem(
|
MuteItem(
|
||||||
not_null<RpWidget*> parent,
|
not_null<RpWidget*> parent,
|
||||||
const style::Menu &st,
|
const style::Menu &st,
|
||||||
not_null<Data::Thread*> thread);
|
Descriptor descriptor);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
@ -79,31 +80,30 @@ private:
|
||||||
const QPoint _itemIconPosition;
|
const QPoint _itemIconPosition;
|
||||||
Ui::Animations::Simple _animation;
|
Ui::Animations::Simple _animation;
|
||||||
bool _isMuted = false;
|
bool _isMuted = false;
|
||||||
|
bool _inited;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
MuteItem::MuteItem(
|
MuteItem::MuteItem(
|
||||||
not_null<RpWidget*> parent,
|
not_null<RpWidget*> parent,
|
||||||
const style::Menu &st,
|
const style::Menu &st,
|
||||||
not_null<Data::Thread*> thread)
|
Descriptor descriptor)
|
||||||
: Ui::Menu::Action(
|
: Ui::Menu::Action(
|
||||||
parent,
|
parent,
|
||||||
st,
|
st,
|
||||||
Ui::CreateChild<QAction>(parent.get()),
|
Ui::CreateChild<QAction>(parent.get()),
|
||||||
nullptr,
|
nullptr,
|
||||||
nullptr)
|
nullptr)
|
||||||
, _itemIconPosition(st.itemIconPosition)
|
, _itemIconPosition(st.itemIconPosition) {
|
||||||
, _isMuted(thread->owner().notifySettings().isMuted(thread)) {
|
descriptor.isMutedValue(
|
||||||
Info::Profile::NotificationsEnabledValue(
|
) | rpl::start_with_next([=](bool isMuted) {
|
||||||
thread
|
|
||||||
) | rpl::start_with_next([=](bool isUnmuted) {
|
|
||||||
const auto isMuted = !isUnmuted;
|
|
||||||
action()->setText(isMuted
|
action()->setText(isMuted
|
||||||
? tr::lng_mute_menu_duration_unmute(tr::now)
|
? tr::lng_mute_menu_duration_unmute(tr::now)
|
||||||
: tr::lng_mute_menu_duration_forever(tr::now));
|
: tr::lng_mute_menu_duration_forever(tr::now));
|
||||||
if (isMuted == _isMuted) {
|
if (_inited && isMuted == _isMuted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
_inited = true;
|
||||||
_isMuted = isMuted;
|
_isMuted = isMuted;
|
||||||
_animation.start(
|
_animation.start(
|
||||||
[=] { update(); },
|
[=] { update(); },
|
||||||
|
@ -112,13 +112,8 @@ MuteItem::MuteItem(
|
||||||
st::defaultPopupMenu.showDuration);
|
st::defaultPopupMenu.showDuration);
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
const auto weak = base::make_weak(thread);
|
|
||||||
setClickedCallback([=] {
|
setClickedCallback([=] {
|
||||||
if (const auto strong = weak.get()) {
|
descriptor.updateMutePeriod(_isMuted ? 0 : kMuteForeverValue);
|
||||||
strong->owner().notifySettings().update(
|
|
||||||
strong,
|
|
||||||
{ .unmute = _isMuted, .forever = !_isMuted });
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +135,7 @@ void MuteItem::paintEvent(QPaintEvent *e) {
|
||||||
icon.paint(p, _itemIconPosition, width(), color);
|
icon.paint(p, _itemIconPosition, width(), color);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MuteBox(not_null<Ui::GenericBox*> box, not_null<Data::Thread*> thread) {
|
void MuteBox(not_null<Ui::GenericBox*> box, Descriptor descriptor) {
|
||||||
struct State {
|
struct State {
|
||||||
int lastSeconds = 0;
|
int lastSeconds = 0;
|
||||||
};
|
};
|
||||||
|
@ -161,14 +156,9 @@ void MuteBox(not_null<Ui::GenericBox*> box, not_null<Data::Thread*> thread) {
|
||||||
: tr::lng_mute_menu_mute();
|
: tr::lng_mute_menu_mute();
|
||||||
}) | rpl::flatten_latest();
|
}) | rpl::flatten_latest();
|
||||||
|
|
||||||
const auto weak = base::make_weak(thread);
|
|
||||||
Ui::ConfirmBox(box, {
|
Ui::ConfirmBox(box, {
|
||||||
.confirmed = [=] {
|
.confirmed = [=] {
|
||||||
if (const auto strong = weak.get()) {
|
descriptor.updateMutePeriod(state->lastSeconds);
|
||||||
strong->owner().notifySettings().update(
|
|
||||||
strong,
|
|
||||||
{ .period = state->lastSeconds });
|
|
||||||
}
|
|
||||||
box->getDelegate()->hideLayer();
|
box->getDelegate()->hideLayer();
|
||||||
},
|
},
|
||||||
.confirmText = std::move(confirmText),
|
.confirmText = std::move(confirmText),
|
||||||
|
@ -178,7 +168,7 @@ void MuteBox(not_null<Ui::GenericBox*> box, not_null<Data::Thread*> thread) {
|
||||||
|
|
||||||
void PickMuteBox(
|
void PickMuteBox(
|
||||||
not_null<Ui::GenericBox*> box,
|
not_null<Ui::GenericBox*> box,
|
||||||
not_null<Data::Thread*> thread) {
|
Descriptor descriptor) {
|
||||||
struct State {
|
struct State {
|
||||||
base::unique_qptr<Ui::PopupMenu> menu;
|
base::unique_qptr<Ui::PopupMenu> menu;
|
||||||
};
|
};
|
||||||
|
@ -191,17 +181,12 @@ void PickMuteBox(
|
||||||
|
|
||||||
const auto pickerCallback = TimePickerBox(box, seconds, phrases, 0);
|
const auto pickerCallback = TimePickerBox(box, seconds, phrases, 0);
|
||||||
|
|
||||||
const auto weak = base::make_weak(thread);
|
|
||||||
Ui::ConfirmBox(box, {
|
Ui::ConfirmBox(box, {
|
||||||
.confirmed = [=] {
|
.confirmed = [=] {
|
||||||
const auto muteFor = pickerCallback();
|
const auto muteFor = pickerCallback();
|
||||||
if (const auto strong = weak.get()) {
|
descriptor.updateMutePeriod(muteFor);
|
||||||
strong->owner().notifySettings().update(
|
descriptor.session->settings().addMutePeriod(muteFor);
|
||||||
strong,
|
descriptor.session->saveSettings();
|
||||||
{ .period = muteFor });
|
|
||||||
strong->session().settings().addMutePeriod(muteFor);
|
|
||||||
strong->session().saveSettings();
|
|
||||||
}
|
|
||||||
box->closeBox();
|
box->closeBox();
|
||||||
},
|
},
|
||||||
.confirmText = tr::lng_mute_menu_mute(),
|
.confirmText = tr::lng_mute_menu_mute(),
|
||||||
|
@ -220,11 +205,7 @@ void PickMuteBox(
|
||||||
st::popupMenuWithIcons);
|
st::popupMenuWithIcons);
|
||||||
state->menu->addAction(
|
state->menu->addAction(
|
||||||
tr::lng_manage_messages_ttl_after_custom(tr::now),
|
tr::lng_manage_messages_ttl_after_custom(tr::now),
|
||||||
[=] {
|
[=] { box->getDelegate()->show(Box(MuteBox, descriptor)); },
|
||||||
if (const auto strong = weak.get()) {
|
|
||||||
box->getDelegate()->show(Box(MuteBox, strong));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
&st::menuIconCustomize);
|
&st::menuIconCustomize);
|
||||||
state->menu->setDestroyedCallback(crl::guard(top, [=] {
|
state->menu->setDestroyedCallback(crl::guard(top, [=] {
|
||||||
top->setForceRippled(false);
|
top->setForceRippled(false);
|
||||||
|
@ -236,46 +217,123 @@ void PickMuteBox(
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
Descriptor ThreadDescriptor(not_null<Data::Thread*> thread) {
|
||||||
|
const auto weak = base::make_weak(thread);
|
||||||
|
const auto isMutedValue = [=]() -> rpl::producer<bool> {
|
||||||
|
if (const auto strong = weak.get()) {
|
||||||
|
return Info::Profile::NotificationsEnabledValue(
|
||||||
|
strong
|
||||||
|
) | rpl::map(!rpl::mappers::_1);
|
||||||
|
}
|
||||||
|
return rpl::single(false);
|
||||||
|
};
|
||||||
|
const auto currentSound = [=] {
|
||||||
|
const auto strong = weak.get();
|
||||||
|
return strong
|
||||||
|
? strong->owner().notifySettings().sound(strong)
|
||||||
|
: std::optional<Data::NotifySound>();
|
||||||
|
};
|
||||||
|
const auto updateSound = crl::guard(weak, [=](Data::NotifySound sound) {
|
||||||
|
thread->owner().notifySettings().update(thread, {}, {}, sound);
|
||||||
|
});
|
||||||
|
const auto updateMutePeriod = crl::guard(weak, [=](TimeId mute) {
|
||||||
|
const auto settings = &thread->owner().notifySettings();
|
||||||
|
if (!mute) {
|
||||||
|
settings->update(thread, { .unmute = true });
|
||||||
|
} else if (mute == kMuteForeverValue) {
|
||||||
|
settings->update(thread, { .forever = true });
|
||||||
|
} else {
|
||||||
|
settings->update(thread, { .period = mute });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
.session = &thread->session(),
|
||||||
|
.isMutedValue = isMutedValue,
|
||||||
|
.currentSound = currentSound,
|
||||||
|
.updateSound = updateSound,
|
||||||
|
.updateMutePeriod = updateMutePeriod,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Descriptor DefaultDescriptor(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
Data::DefaultNotify type) {
|
||||||
|
const auto settings = &session->data().notifySettings();
|
||||||
|
const auto isMutedValue = [=]() -> rpl::producer<bool> {
|
||||||
|
return rpl::single(
|
||||||
|
rpl::empty
|
||||||
|
) | rpl::then(
|
||||||
|
settings->defaultUpdates(type)
|
||||||
|
) | rpl::map([=] {
|
||||||
|
return settings->isMuted(type);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const auto currentSound = [=] {
|
||||||
|
return settings->defaultSettings(type).sound();
|
||||||
|
};
|
||||||
|
const auto updateSound = [=](Data::NotifySound sound) {
|
||||||
|
settings->defaultUpdate(type, {}, {}, sound);
|
||||||
|
};
|
||||||
|
const auto updateMutePeriod = [=](TimeId mute) {
|
||||||
|
if (!mute) {
|
||||||
|
settings->defaultUpdate(type, { .unmute = true });
|
||||||
|
} else if (mute == kMuteForeverValue) {
|
||||||
|
settings->defaultUpdate(type, { .forever = true });
|
||||||
|
} else {
|
||||||
|
settings->defaultUpdate(type, { .period = mute });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
.session = session,
|
||||||
|
.isMutedValue = isMutedValue,
|
||||||
|
.currentSound = currentSound,
|
||||||
|
.updateSound = updateSound,
|
||||||
|
.updateMutePeriod = updateMutePeriod,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void FillMuteMenu(
|
void FillMuteMenu(
|
||||||
not_null<Ui::PopupMenu*> menu,
|
not_null<Ui::PopupMenu*> menu,
|
||||||
not_null<Data::Thread*> thread,
|
Descriptor descriptor,
|
||||||
std::shared_ptr<Ui::Show> show) {
|
std::shared_ptr<Ui::Show> show) {
|
||||||
const auto weak = base::make_weak(thread);
|
const auto session = descriptor.session;
|
||||||
const auto with = [=](Fn<void(not_null<Data::Thread*> thread)> handler) {
|
const auto soundSelect = [=] {
|
||||||
return [=] {
|
if (const auto currentSound = descriptor.currentSound()) {
|
||||||
if (const auto strong = weak.get()) {
|
show->showBox(Box(
|
||||||
handler(strong);
|
RingtonesBox,
|
||||||
}
|
session,
|
||||||
};
|
*currentSound,
|
||||||
|
descriptor.updateSound));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
menu->addAction(
|
menu->addAction(
|
||||||
tr::lng_mute_menu_sound_select(tr::now),
|
tr::lng_mute_menu_sound_select(tr::now),
|
||||||
with([=](not_null<Data::Thread*> thread) {
|
soundSelect,
|
||||||
show->showBox(Box(ThreadRingtonesBox, thread));
|
|
||||||
}),
|
|
||||||
&st::menuIconSoundSelect);
|
&st::menuIconSoundSelect);
|
||||||
|
|
||||||
const auto notifySettings = &thread->owner().notifySettings();
|
const auto soundIsNone = descriptor.currentSound().value_or(
|
||||||
const auto soundIsNone = notifySettings->sound(thread).none;
|
Data::NotifySound()
|
||||||
|
).none;
|
||||||
|
const auto toggleSound = [=] {
|
||||||
|
if (auto sound = descriptor.currentSound()) {
|
||||||
|
sound->none = !soundIsNone;
|
||||||
|
descriptor.updateSound(*sound);
|
||||||
|
}
|
||||||
|
};
|
||||||
menu->addAction(
|
menu->addAction(
|
||||||
soundIsNone
|
(soundIsNone
|
||||||
? tr::lng_mute_menu_sound_on(tr::now)
|
? tr::lng_mute_menu_sound_on(tr::now)
|
||||||
: tr::lng_mute_menu_sound_off(tr::now),
|
: tr::lng_mute_menu_sound_off(tr::now)),
|
||||||
with([=](not_null<Data::Thread*> thread) {
|
toggleSound,
|
||||||
auto sound = notifySettings->sound(thread);
|
|
||||||
sound.none = !sound.none;
|
|
||||||
notifySettings->update(thread, {}, {}, sound);
|
|
||||||
}),
|
|
||||||
soundIsNone ? &st::menuIconSoundOn : &st::menuIconSoundOff);
|
soundIsNone ? &st::menuIconSoundOn : &st::menuIconSoundOff);
|
||||||
|
|
||||||
const auto &st = menu->st().menu;
|
const auto &st = menu->st().menu;
|
||||||
const auto iconTextPosition = st.itemIconPosition
|
const auto iconTextPosition = st.itemIconPosition
|
||||||
+ st::menuIconMuteForAnyTextPosition;
|
+ st::menuIconMuteForAnyTextPosition;
|
||||||
for (const auto muteFor : thread->session().settings().mutePeriods()) {
|
for (const auto muteFor : session->settings().mutePeriods()) {
|
||||||
const auto callback = with([=](not_null<Data::Thread*> thread) {
|
const auto callback = [=, update = descriptor.updateMutePeriod] {
|
||||||
notifySettings->update(thread, { .period = muteFor });
|
update(muteFor);
|
||||||
});
|
};
|
||||||
|
|
||||||
auto item = base::make_unique_q<IconWithText>(
|
auto item = base::make_unique_q<IconWithText>(
|
||||||
menu,
|
menu,
|
||||||
|
@ -295,20 +353,17 @@ void FillMuteMenu(
|
||||||
|
|
||||||
menu->addAction(
|
menu->addAction(
|
||||||
tr::lng_mute_menu_duration(tr::now),
|
tr::lng_mute_menu_duration(tr::now),
|
||||||
with([=](not_null<Data::Thread*> thread) {
|
[=] { show->showBox(Box(PickMuteBox, descriptor)); },
|
||||||
DEBUG_LOG(("Mute Info: PickMuteBox called."));
|
|
||||||
show->showBox(Box(PickMuteBox, thread));
|
|
||||||
}),
|
|
||||||
&st::menuIconMuteFor);
|
&st::menuIconMuteFor);
|
||||||
|
|
||||||
menu->addAction(
|
menu->addAction(
|
||||||
base::make_unique_q<MuteItem>(menu, menu->st().menu, thread));
|
base::make_unique_q<MuteItem>(menu, menu->st().menu, descriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupMuteMenu(
|
void SetupMuteMenu(
|
||||||
not_null<Ui::RpWidget*> parent,
|
not_null<Ui::RpWidget*> parent,
|
||||||
rpl::producer<> triggers,
|
rpl::producer<> triggers,
|
||||||
Fn<Data::Thread*()> makeThread,
|
Fn<std::optional<Descriptor>()> makeDescriptor,
|
||||||
std::shared_ptr<Ui::Show> show) {
|
std::shared_ptr<Ui::Show> show) {
|
||||||
struct State {
|
struct State {
|
||||||
base::unique_qptr<Ui::PopupMenu> menu;
|
base::unique_qptr<Ui::PopupMenu> menu;
|
||||||
|
@ -319,11 +374,11 @@ void SetupMuteMenu(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
if (state->menu) {
|
if (state->menu) {
|
||||||
return;
|
return;
|
||||||
} else if (const auto thread = makeThread()) {
|
} else if (const auto descriptor = makeDescriptor()) {
|
||||||
state->menu = base::make_unique_q<Ui::PopupMenu>(
|
state->menu = base::make_unique_q<Ui::PopupMenu>(
|
||||||
parent,
|
parent,
|
||||||
st::popupMenuWithIcons);
|
st::popupMenuWithIcons);
|
||||||
FillMuteMenu(state->menu.get(), thread, show);
|
FillMuteMenu(state->menu.get(), *descriptor, show);
|
||||||
state->menu->popup(QCursor::pos());
|
state->menu->popup(QCursor::pos());
|
||||||
}
|
}
|
||||||
}, parent->lifetime());
|
}, parent->lifetime());
|
||||||
|
|
|
@ -9,8 +9,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
class Thread;
|
class Thread;
|
||||||
|
struct NotifySound;
|
||||||
|
enum class DefaultNotify;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
|
namespace Main {
|
||||||
|
class Session;
|
||||||
|
} // namespace Main
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class PopupMenu;
|
class PopupMenu;
|
||||||
class RpWidget;
|
class RpWidget;
|
||||||
|
@ -19,15 +25,48 @@ class Show;
|
||||||
|
|
||||||
namespace MuteMenu {
|
namespace MuteMenu {
|
||||||
|
|
||||||
|
struct Descriptor {
|
||||||
|
not_null<Main::Session*> session;
|
||||||
|
Fn<rpl::producer<bool>()> isMutedValue;
|
||||||
|
Fn<std::optional<Data::NotifySound>()> currentSound;
|
||||||
|
Fn<void(Data::NotifySound)> updateSound;
|
||||||
|
Fn<void(TimeId)> updateMutePeriod;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] Descriptor ThreadDescriptor(not_null<Data::Thread*> thread);
|
||||||
|
[[nodiscard]] Descriptor DefaultDescriptor(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
Data::DefaultNotify type);
|
||||||
|
|
||||||
void FillMuteMenu(
|
void FillMuteMenu(
|
||||||
not_null<Ui::PopupMenu*> menu,
|
not_null<Ui::PopupMenu*> menu,
|
||||||
not_null<Data::Thread*> thread,
|
Descriptor descriptor,
|
||||||
std::shared_ptr<Ui::Show> show);
|
std::shared_ptr<Ui::Show> show);
|
||||||
|
|
||||||
void SetupMuteMenu(
|
void SetupMuteMenu(
|
||||||
not_null<Ui::RpWidget*> parent,
|
not_null<Ui::RpWidget*> parent,
|
||||||
rpl::producer<> triggers,
|
rpl::producer<> triggers,
|
||||||
Fn<Data::Thread*()> makeThread,
|
Fn<std::optional<Descriptor>()> makeDescriptor,
|
||||||
std::shared_ptr<Ui::Show> show);
|
std::shared_ptr<Ui::Show> show);
|
||||||
|
|
||||||
|
inline void FillMuteMenu(
|
||||||
|
not_null<Ui::PopupMenu*> menu,
|
||||||
|
not_null<Data::Thread*> thread,
|
||||||
|
std::shared_ptr<Ui::Show> show) {
|
||||||
|
FillMuteMenu(menu, ThreadDescriptor(thread), std::move(show));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void SetupMuteMenu(
|
||||||
|
not_null<Ui::RpWidget*> parent,
|
||||||
|
rpl::producer<> triggers,
|
||||||
|
Fn<Data::Thread*()> makeThread,
|
||||||
|
std::shared_ptr<Ui::Show> show) {
|
||||||
|
SetupMuteMenu(parent, std::move(triggers), [=] {
|
||||||
|
const auto thread = makeThread();
|
||||||
|
return thread
|
||||||
|
? ThreadDescriptor(thread)
|
||||||
|
: std::optional<Descriptor>();
|
||||||
|
}, std::move(show));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace MuteMenu
|
} // namespace MuteMenu
|
||||||
|
|
|
@ -10,8 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/platform/base_platform_info.h"
|
#include "base/platform/base_platform_info.h"
|
||||||
#include "base/platform/linux/base_linux_xdp_utilities.h"
|
#include "base/platform/linux/base_linux_xdp_utilities.h"
|
||||||
#include "base/platform/linux/base_linux_wayland_integration.h"
|
#include "base/platform/linux/base_linux_wayland_integration.h"
|
||||||
#include "core/application.h"
|
|
||||||
#include "window/window_controller.h"
|
|
||||||
#include "base/random.h"
|
#include "base/random.h"
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
@ -60,26 +58,13 @@ bool ShowXDPOpenWithDialog(const QString &filepath) {
|
||||||
|
|
||||||
const auto fdGuard = gsl::finally([&] { ::close(fd); });
|
const auto fdGuard = gsl::finally([&] { ::close(fd); });
|
||||||
|
|
||||||
const auto parentWindowId = [&]() -> Glib::ustring {
|
|
||||||
const auto activeWindow = Core::App().activeWindow();
|
|
||||||
if (!activeWindow) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return base::Platform::XDP::ParentWindowID(
|
|
||||||
activeWindow->widget()->windowHandle());
|
|
||||||
}();
|
|
||||||
|
|
||||||
const auto handleToken = Glib::ustring("tdesktop")
|
const auto handleToken = Glib::ustring("tdesktop")
|
||||||
+ std::to_string(base::RandomValue<uint>());
|
+ std::to_string(base::RandomValue<uint>());
|
||||||
|
|
||||||
const auto activationToken = []() -> Glib::ustring {
|
const auto activationToken = []() -> Glib::ustring {
|
||||||
using base::Platform::WaylandIntegration;
|
using base::Platform::WaylandIntegration;
|
||||||
if (const auto integration = WaylandIntegration::Instance()) {
|
if (const auto integration = WaylandIntegration::Instance()) {
|
||||||
if (const auto token = integration->activationToken()
|
return integration->activationToken().toStdString();
|
||||||
; !token.isNull()) {
|
|
||||||
return token.toStdString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}();
|
}();
|
||||||
|
@ -124,7 +109,7 @@ bool ShowXDPOpenWithDialog(const QString &filepath) {
|
||||||
kXDPOpenURIInterface,
|
kXDPOpenURIInterface,
|
||||||
"OpenFile",
|
"OpenFile",
|
||||||
Glib::create_variant(std::tuple{
|
Glib::create_variant(std::tuple{
|
||||||
parentWindowId,
|
base::Platform::XDP::ParentWindowID(),
|
||||||
Glib::DBusHandle(),
|
Glib::DBusHandle(),
|
||||||
std::map<Glib::ustring, Glib::VariantBase>{
|
std::map<Glib::ustring, Glib::VariantBase>{
|
||||||
{
|
{
|
||||||
|
|
|
@ -139,11 +139,13 @@ void XCBSetDesktopFileName(QWindow *window) {
|
||||||
void SkipTaskbar(QWindow *window, bool skip) {
|
void SkipTaskbar(QWindow *window, bool skip) {
|
||||||
if (const auto integration = WaylandIntegration::Instance()) {
|
if (const auto integration = WaylandIntegration::Instance()) {
|
||||||
integration->skipTaskbar(window, skip);
|
integration->skipTaskbar(window, skip);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||||
if (IsX11()) {
|
if (IsX11()) {
|
||||||
XCBSkipTaskbar(window, skip);
|
XCBSkipTaskbar(window, skip);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,10 +17,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "storage/localstorage.h"
|
#include "storage/localstorage.h"
|
||||||
#include "core/launcher.h"
|
#include "core/launcher.h"
|
||||||
#include "core/application.h"
|
|
||||||
#include "core/core_settings.h"
|
#include "core/core_settings.h"
|
||||||
#include "core/update_checker.h"
|
#include "core/update_checker.h"
|
||||||
#include "window/window_controller.h"
|
|
||||||
#include "webview/platform/linux/webview_linux_webkitgtk.h"
|
#include "webview/platform/linux/webview_linux_webkitgtk.h"
|
||||||
|
|
||||||
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||||
|
@ -65,16 +63,6 @@ bool PortalAutostart(bool start, bool silent) {
|
||||||
const auto connection = Gio::DBus::Connection::get_sync(
|
const auto connection = Gio::DBus::Connection::get_sync(
|
||||||
Gio::DBus::BusType::SESSION);
|
Gio::DBus::BusType::SESSION);
|
||||||
|
|
||||||
const auto parentWindowId = [&]() -> Glib::ustring {
|
|
||||||
const auto activeWindow = Core::App().activeWindow();
|
|
||||||
if (!activeWindow) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return base::Platform::XDP::ParentWindowID(
|
|
||||||
activeWindow->widget()->windowHandle());
|
|
||||||
}();
|
|
||||||
|
|
||||||
const auto handleToken = Glib::ustring("tdesktop")
|
const auto handleToken = Glib::ustring("tdesktop")
|
||||||
+ std::to_string(base::RandomValue<uint>());
|
+ std::to_string(base::RandomValue<uint>());
|
||||||
|
|
||||||
|
@ -152,7 +140,7 @@ bool PortalAutostart(bool start, bool silent) {
|
||||||
"org.freedesktop.portal.Background",
|
"org.freedesktop.portal.Background",
|
||||||
"RequestBackground",
|
"RequestBackground",
|
||||||
Glib::create_variant(std::tuple{
|
Glib::create_variant(std::tuple{
|
||||||
parentWindowId,
|
base::Platform::XDP::ParentWindowID(),
|
||||||
options,
|
options,
|
||||||
}),
|
}),
|
||||||
base::Platform::XDP::kService);
|
base::Platform::XDP::kService);
|
||||||
|
|
|
@ -523,10 +523,19 @@ settingsPremiumLock: icon{{ "emoji/premium_lock", windowActiveTextFg, point(0px,
|
||||||
settingsPremiumLockSkip: 3px;
|
settingsPremiumLockSkip: 3px;
|
||||||
|
|
||||||
settingsBlockedListSubtitleAddPadding: margins(0px, 1px, 0px, -4px);
|
settingsBlockedListSubtitleAddPadding: margins(0px, 1px, 0px, -4px);
|
||||||
settingsBlockedListIconPadding: margins(0px, 34px, 0px, 5px);
|
settingsBlockedListIconPadding: margins(0px, 24px, 0px, 5px);
|
||||||
settingsBlockedList: PeerList(peerListBox) {
|
settingsBlockedList: PeerList(peerListBox) {
|
||||||
padding: margins(0px, 0px, 0px, membersMarginBottom);
|
padding: margins(0px, 0px, 0px, membersMarginBottom);
|
||||||
}
|
}
|
||||||
|
settingsBlockedHeightMin: 240px;
|
||||||
|
|
||||||
|
settingsNotificationType: SettingsButton(settingsButton) {
|
||||||
|
height: 40px;
|
||||||
|
padding: margins(60px, 4px, 22px, 4px);
|
||||||
|
}
|
||||||
|
settingsNotificationTypeDetails: FlatLabel(defaultFlatLabel) {
|
||||||
|
textFg: windowSubTextFg;
|
||||||
|
}
|
||||||
|
|
||||||
requestPeerRestriction: FlatLabel(defaultFlatLabel) {
|
requestPeerRestriction: FlatLabel(defaultFlatLabel) {
|
||||||
minWidth: 240px;
|
minWidth: 240px;
|
||||||
|
|
|
@ -47,7 +47,10 @@ Blocked::Blocked(
|
||||||
tr::lng_contacts_loading(),
|
tr::lng_contacts_loading(),
|
||||||
st::changePhoneDescription),
|
st::changePhoneDescription),
|
||||||
std::move(padding)));
|
std::move(padding)));
|
||||||
Ui::ResizeFitChild(this, _loading.get());
|
Ui::ResizeFitChild(
|
||||||
|
this,
|
||||||
|
_loading.get(),
|
||||||
|
st::settingsBlockedHeightMin);
|
||||||
}
|
}
|
||||||
|
|
||||||
_controller->session().api().blockedPeers().slice(
|
_controller->session().api().blockedPeers().slice(
|
||||||
|
@ -77,7 +80,7 @@ QPointer<Ui::RpWidget> Blocked::createPinnedToTop(not_null<QWidget*> parent) {
|
||||||
content,
|
content,
|
||||||
tr::lng_blocked_list_add(),
|
tr::lng_blocked_list_add(),
|
||||||
st::settingsButtonActive,
|
st::settingsButtonActive,
|
||||||
{ &st::menuIconBlockSettings, IconType::Round, &st::transparent }
|
{ &st::menuIconBlockSettings }
|
||||||
)->addClickHandler([=] {
|
)->addClickHandler([=] {
|
||||||
BlockedBoxController::BlockNewPeer(_controller);
|
BlockedBoxController::BlockNewPeer(_controller);
|
||||||
});
|
});
|
||||||
|
@ -201,7 +204,30 @@ void Blocked::setupContent() {
|
||||||
AddSkip(content, st::settingsBlockedListIconPadding.top());
|
AddSkip(content, st::settingsBlockedListIconPadding.top());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ui::ResizeFitChild(this, _container);
|
// We want minimal height to be the same no matter if subtitle
|
||||||
|
// is visible or not, so minimal height isn't a constant here.
|
||||||
|
// Ui::ResizeFitChild(this, _container, st::settingsBlockedHeightMin);
|
||||||
|
|
||||||
|
widthValue(
|
||||||
|
) | rpl::start_with_next([=](int width) {
|
||||||
|
_container->resizeToWidth(width);
|
||||||
|
}, _container->lifetime());
|
||||||
|
|
||||||
|
rpl::combine(
|
||||||
|
_container->heightValue(),
|
||||||
|
_emptinessChanges.events_starting_with(true)
|
||||||
|
) | rpl::start_with_next([=](int height, bool empty) {
|
||||||
|
const auto subtitled = !empty || (_countBlocked.current() > 0);
|
||||||
|
const auto total = st::settingsBlockedHeightMin;
|
||||||
|
const auto padding = st::settingsSubsectionTitlePadding
|
||||||
|
+ st::settingsBlockedListSubtitleAddPadding;
|
||||||
|
const auto subtitle = st::settingsSectionSkip
|
||||||
|
+ padding.top()
|
||||||
|
+ st::settingsSubsectionTitle.style.font->height
|
||||||
|
+ padding.bottom();
|
||||||
|
const auto min = total - (subtitled ? subtitle : 0);
|
||||||
|
resize(width(), std::max(height, min));
|
||||||
|
}, _container->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Blocked::checkTotal(int total) {
|
void Blocked::checkTotal(int total) {
|
||||||
|
|
|
@ -8,8 +8,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "settings/settings_notifications.h"
|
#include "settings/settings_notifications.h"
|
||||||
|
|
||||||
#include "settings/settings_common.h"
|
#include "settings/settings_common.h"
|
||||||
|
#include "settings/settings_notifications_type.h"
|
||||||
|
#include "ui/boxes/confirm_box.h"
|
||||||
#include "ui/controls/chat_service_checkbox.h"
|
#include "ui/controls/chat_service_checkbox.h"
|
||||||
#include "ui/effects/animations.h"
|
#include "ui/effects/animations.h"
|
||||||
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
#include "ui/wrap/slide_wrap.h"
|
#include "ui/wrap/slide_wrap.h"
|
||||||
#include "ui/widgets/box_content_divider.h"
|
#include "ui/widgets/box_content_divider.h"
|
||||||
|
@ -140,6 +143,160 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void AddTypeButton(
|
||||||
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
not_null<Window::SessionController*> controller,
|
||||||
|
Data::DefaultNotify type,
|
||||||
|
Fn<void(Type)> showOther) {
|
||||||
|
using Type = Data::DefaultNotify;
|
||||||
|
auto label = [&] {
|
||||||
|
switch (type) {
|
||||||
|
case Type::User: return tr::lng_notification_private_chats();
|
||||||
|
case Type::Group: return tr::lng_notification_groups();
|
||||||
|
case Type::Broadcast: return tr::lng_notification_channels();
|
||||||
|
}
|
||||||
|
Unexpected("Type value in AddTypeButton.");
|
||||||
|
}();
|
||||||
|
const auto icon = [&] {
|
||||||
|
switch (type) {
|
||||||
|
case Type::User: return &st::menuIconProfile;
|
||||||
|
case Type::Group: return &st::menuIconGroups;
|
||||||
|
case Type::Broadcast: return &st::menuIconChannel;
|
||||||
|
}
|
||||||
|
Unexpected("Type value in AddTypeButton.");
|
||||||
|
}();
|
||||||
|
const auto button = AddButton(
|
||||||
|
container,
|
||||||
|
std::move(label),
|
||||||
|
st::settingsNotificationType,
|
||||||
|
{ icon });
|
||||||
|
button->setClickedCallback([=] {
|
||||||
|
showOther(NotificationsTypeId(type));
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto session = &controller->session();
|
||||||
|
const auto settings = &session->data().notifySettings();
|
||||||
|
const auto &st = st::settingsNotificationType;
|
||||||
|
auto status = rpl::combine(
|
||||||
|
NotificationsEnabledForTypeValue(session, type),
|
||||||
|
rpl::single(
|
||||||
|
type
|
||||||
|
) | rpl::then(settings->exceptionsUpdates(
|
||||||
|
) | rpl::filter(rpl::mappers::_1 == type))
|
||||||
|
) | rpl::map([=](bool enabled, const auto &) {
|
||||||
|
const auto count = int(settings->exceptions(type).size());
|
||||||
|
return !count
|
||||||
|
? tr::lng_notification_click_to_change()
|
||||||
|
: (enabled
|
||||||
|
? tr::lng_notification_on
|
||||||
|
: tr::lng_notification_off)(
|
||||||
|
lt_exceptions,
|
||||||
|
tr::lng_notification_exceptions(
|
||||||
|
lt_count,
|
||||||
|
rpl::single(float64(count))));
|
||||||
|
}) | rpl::flatten_latest();
|
||||||
|
const auto details = Ui::CreateChild<Ui::FlatLabel>(
|
||||||
|
button.get(),
|
||||||
|
std::move(status),
|
||||||
|
st::settingsNotificationTypeDetails);
|
||||||
|
details->show();
|
||||||
|
details->moveToLeft(
|
||||||
|
st.padding.left(),
|
||||||
|
st.padding.top() + st.height - details->height());
|
||||||
|
details->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
|
||||||
|
const auto toggleButton = Ui::CreateChild<Ui::SettingsButton>(
|
||||||
|
container.get(),
|
||||||
|
nullptr,
|
||||||
|
st);
|
||||||
|
const auto checkView = button->lifetime().make_state<Ui::ToggleView>(
|
||||||
|
st.toggle,
|
||||||
|
NotificationsEnabledForType(session, type),
|
||||||
|
[=] { toggleButton->update(); });
|
||||||
|
|
||||||
|
const auto separator = Ui::CreateChild<Ui::RpWidget>(container.get());
|
||||||
|
separator->paintRequest(
|
||||||
|
) | rpl::start_with_next([=, bg = st.textBgOver] {
|
||||||
|
auto p = QPainter(separator);
|
||||||
|
p.fillRect(separator->rect(), bg);
|
||||||
|
}, separator->lifetime());
|
||||||
|
const auto separatorHeight = st.height - 2 * st.toggle.border;
|
||||||
|
button->geometryValue(
|
||||||
|
) | rpl::start_with_next([=](const QRect &r) {
|
||||||
|
const auto w = st::rightsButtonToggleWidth;
|
||||||
|
toggleButton->setGeometry(
|
||||||
|
r.x() + r.width() - w,
|
||||||
|
r.y(),
|
||||||
|
w,
|
||||||
|
r.height());
|
||||||
|
separator->setGeometry(
|
||||||
|
toggleButton->x() - st::lineWidth,
|
||||||
|
r.y() + (r.height() - separatorHeight) / 2,
|
||||||
|
st::lineWidth,
|
||||||
|
separatorHeight);
|
||||||
|
}, toggleButton->lifetime());
|
||||||
|
|
||||||
|
const auto checkWidget = Ui::CreateChild<Ui::RpWidget>(toggleButton);
|
||||||
|
checkWidget->resize(checkView->getSize());
|
||||||
|
checkWidget->paintRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
auto p = QPainter(checkWidget);
|
||||||
|
checkView->paint(p, 0, 0, checkWidget->width());
|
||||||
|
}, checkWidget->lifetime());
|
||||||
|
toggleButton->sizeValue(
|
||||||
|
) | rpl::start_with_next([=](const QSize &s) {
|
||||||
|
checkWidget->moveToRight(
|
||||||
|
st.toggleSkip,
|
||||||
|
(s.height() - checkWidget->height()) / 2);
|
||||||
|
}, toggleButton->lifetime());
|
||||||
|
|
||||||
|
const auto toggle = crl::guard(toggleButton, [=] {
|
||||||
|
const auto enabled = !checkView->checked();
|
||||||
|
checkView->setChecked(enabled, anim::type::normal);
|
||||||
|
settings->defaultUpdate(type, Data::MuteValue{
|
||||||
|
.unmute = enabled,
|
||||||
|
.forever = !enabled,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
toggleButton->clicks(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
const auto count = int(settings->exceptions(type).size());
|
||||||
|
if (!count) {
|
||||||
|
toggle();
|
||||||
|
} else {
|
||||||
|
controller->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||||
|
const auto phrase = [&] {
|
||||||
|
switch (type) {
|
||||||
|
case Type::User:
|
||||||
|
return tr::lng_notification_about_private_chats;
|
||||||
|
case Type::Group:
|
||||||
|
return tr::lng_notification_about_groups;
|
||||||
|
case Type::Broadcast:
|
||||||
|
return tr::lng_notification_about_channels;
|
||||||
|
}
|
||||||
|
Unexpected("Type in AddTypeButton.");
|
||||||
|
}();
|
||||||
|
Ui::ConfirmBox(box, {
|
||||||
|
.text = phrase(
|
||||||
|
lt_count,
|
||||||
|
rpl::single(float64(count)),
|
||||||
|
Ui::Text::RichLangValue),
|
||||||
|
.confirmed = [=](auto close) { toggle(); close(); },
|
||||||
|
.confirmText = tr::lng_box_ok(),
|
||||||
|
.title = tr::lng_notification_exceptions_title(),
|
||||||
|
.inform = true,
|
||||||
|
});
|
||||||
|
box->addLeftButton(
|
||||||
|
tr::lng_notification_exceptions_view(),
|
||||||
|
[=] {
|
||||||
|
box->closeBox();
|
||||||
|
showOther(NotificationsTypeId(type));
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}, toggleButton->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
NotificationsCount::NotificationsCount(
|
NotificationsCount::NotificationsCount(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
not_null<Window::SessionController*> controller)
|
not_null<Window::SessionController*> controller)
|
||||||
|
@ -817,15 +974,16 @@ void SetupMultiAccountNotifications(
|
||||||
|
|
||||||
void SetupNotificationsContent(
|
void SetupNotificationsContent(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
not_null<Ui::VerticalLayout*> container) {
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
Fn<void(Type)> showOther) {
|
||||||
using namespace rpl::mappers;
|
using namespace rpl::mappers;
|
||||||
|
|
||||||
AddSkip(container);
|
AddSkip(container, st::settingsPrivacySkip);
|
||||||
|
|
||||||
using NotifyView = Core::Settings::NotifyView;
|
using NotifyView = Core::Settings::NotifyView;
|
||||||
SetupMultiAccountNotifications(controller, container);
|
SetupMultiAccountNotifications(controller, container);
|
||||||
|
|
||||||
AddSubsectionTitle(container, tr::lng_settings_notify_title());
|
AddSubsectionTitle(container, tr::lng_settings_notify_global());
|
||||||
|
|
||||||
const auto session = &controller->session();
|
const auto session = &controller->session();
|
||||||
const auto checkbox = [&](
|
const auto checkbox = [&](
|
||||||
|
@ -871,41 +1029,15 @@ void SetupNotificationsContent(
|
||||||
flashbounceToggles->events_starting_with(
|
flashbounceToggles->events_starting_with(
|
||||||
settings.flashBounceNotify()));
|
settings.flashBounceNotify()));
|
||||||
|
|
||||||
const auto soundLabel = container->lifetime(
|
const auto soundAllowed = container->lifetime(
|
||||||
).make_state<rpl::event_stream<QString>>();
|
).make_state<rpl::event_stream<bool>>();
|
||||||
const auto soundValue = [=] {
|
const auto allowed = [=] {
|
||||||
const auto owner = &controller->session().data();
|
return Core::App().settings().soundNotify();
|
||||||
const auto &settings = owner->notifySettings().defaultSettings(
|
|
||||||
Data::DefaultNotify::User);
|
|
||||||
return !Core::App().settings().soundNotify()
|
|
||||||
? Data::NotifySound{ .none = true }
|
|
||||||
: settings.sound().value_or(Data::NotifySound());
|
|
||||||
};
|
};
|
||||||
const auto label = [=] {
|
const auto sound = addCheckbox(
|
||||||
const auto now = soundValue();
|
tr::lng_settings_sound_allowed(),
|
||||||
const auto owner = &controller->session().data();
|
{ &st::menuIconUnmute },
|
||||||
return now.none
|
soundAllowed->events_starting_with(allowed()));
|
||||||
? tr::lng_settings_sound_notify_off(tr::now)
|
|
||||||
: !now.id
|
|
||||||
? tr::lng_ringtones_box_default(tr::now)
|
|
||||||
: ExtractRingtoneName(owner->document(now.id));
|
|
||||||
};
|
|
||||||
controller->session().data().notifySettings().defaultUpdates(
|
|
||||||
Data::DefaultNotify::User
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
soundLabel->fire(label());
|
|
||||||
}, container->lifetime());
|
|
||||||
controller->session().api().ringtones().listUpdates(
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
soundLabel->fire(label());
|
|
||||||
}, container->lifetime());
|
|
||||||
|
|
||||||
const auto sound = AddButtonWithLabel(
|
|
||||||
container,
|
|
||||||
tr::lng_settings_sound_notify(),
|
|
||||||
soundLabel->events_starting_with(label()),
|
|
||||||
st::settingsButton,
|
|
||||||
{ &st::menuIconSoundOn });
|
|
||||||
|
|
||||||
AddSkip(container);
|
AddSkip(container);
|
||||||
|
|
||||||
|
@ -924,7 +1056,20 @@ void SetupNotificationsContent(
|
||||||
previewWrap->toggle(settings.desktopNotify(), anim::type::instant);
|
previewWrap->toggle(settings.desktopNotify(), anim::type::instant);
|
||||||
previewDivider->toggle(!settings.desktopNotify(), anim::type::instant);
|
previewDivider->toggle(!settings.desktopNotify(), anim::type::instant);
|
||||||
|
|
||||||
|
controller->session().data().notifySettings().loadExceptions();
|
||||||
|
|
||||||
AddSkip(container, st::notifyPreviewBottomSkip);
|
AddSkip(container, st::notifyPreviewBottomSkip);
|
||||||
|
AddSubsectionTitle(container, tr::lng_settings_notify_title());
|
||||||
|
const auto addType = [&](Data::DefaultNotify type) {
|
||||||
|
AddTypeButton(container, controller, type, showOther);
|
||||||
|
};
|
||||||
|
addType(Data::DefaultNotify::User);
|
||||||
|
addType(Data::DefaultNotify::Group);
|
||||||
|
addType(Data::DefaultNotify::Broadcast);
|
||||||
|
|
||||||
|
AddSkip(container, st::settingsCheckboxesSkip);
|
||||||
|
AddDivider(container);
|
||||||
|
AddSkip(container, st::settingsCheckboxesSkip);
|
||||||
AddSubsectionTitle(container, tr::lng_settings_events_title());
|
AddSubsectionTitle(container, tr::lng_settings_events_title());
|
||||||
|
|
||||||
auto joinSilent = rpl::single(
|
auto joinSilent = rpl::single(
|
||||||
|
@ -1045,6 +1190,14 @@ void SetupNotificationsContent(
|
||||||
changed(Change::DesktopEnabled);
|
changed(Change::DesktopEnabled);
|
||||||
}, desktop->lifetime());
|
}, desktop->lifetime());
|
||||||
|
|
||||||
|
sound->toggledChanges(
|
||||||
|
) | rpl::filter([](bool checked) {
|
||||||
|
return (checked != Core::App().settings().soundNotify());
|
||||||
|
}) | rpl::start_with_next([=](bool checked) {
|
||||||
|
Core::App().settings().setSoundNotify(checked);
|
||||||
|
changed(Change::SoundEnabled);
|
||||||
|
}, sound->lifetime());
|
||||||
|
|
||||||
name->checkedChanges(
|
name->checkedChanges(
|
||||||
) | rpl::map([=](bool checked) {
|
) | rpl::map([=](bool checked) {
|
||||||
if (!checked) {
|
if (!checked) {
|
||||||
|
@ -1077,25 +1230,6 @@ void SetupNotificationsContent(
|
||||||
changed(Change::ViewParams);
|
changed(Change::ViewParams);
|
||||||
}, preview->lifetime());
|
}, preview->lifetime());
|
||||||
|
|
||||||
sound->setClickedCallback([=] {
|
|
||||||
controller->show(Box(RingtonesBox, session, soundValue(), [=](
|
|
||||||
Data::NotifySound sound) {
|
|
||||||
Core::App().settings().setSoundNotify(!sound.none);
|
|
||||||
if (!sound.none) {
|
|
||||||
using Type = Data::DefaultNotify;
|
|
||||||
const auto owner = &controller->session().data();
|
|
||||||
auto &settings = owner->notifySettings();
|
|
||||||
const auto updateType = [&](Type type) {
|
|
||||||
settings.defaultUpdate(type, {}, {}, sound);
|
|
||||||
};
|
|
||||||
updateType(Type::User);
|
|
||||||
updateType(Type::Group);
|
|
||||||
updateType(Type::Broadcast);
|
|
||||||
}
|
|
||||||
changed(Change::SoundEnabled);
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
flashbounce->toggledChanges(
|
flashbounce->toggledChanges(
|
||||||
) | rpl::filter([](bool checked) {
|
) | rpl::filter([](bool checked) {
|
||||||
return (checked != Core::App().settings().flashBounceNotify());
|
return (checked != Core::App().settings().flashBounceNotify());
|
||||||
|
@ -1133,7 +1267,7 @@ void SetupNotificationsContent(
|
||||||
} else if (change == Change::ViewParams) {
|
} else if (change == Change::ViewParams) {
|
||||||
//
|
//
|
||||||
} else if (change == Change::SoundEnabled) {
|
} else if (change == Change::SoundEnabled) {
|
||||||
soundLabel->fire(label());
|
soundAllowed->fire(allowed());
|
||||||
} else if (change == Change::FlashBounceEnabled) {
|
} else if (change == Change::FlashBounceEnabled) {
|
||||||
flashbounceToggles->fire(
|
flashbounceToggles->fire(
|
||||||
Core::App().settings().flashBounceNotify());
|
Core::App().settings().flashBounceNotify());
|
||||||
|
@ -1160,8 +1294,9 @@ void SetupNotificationsContent(
|
||||||
|
|
||||||
void SetupNotifications(
|
void SetupNotifications(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
not_null<Ui::VerticalLayout*> container) {
|
not_null<Ui::VerticalLayout*> container,
|
||||||
SetupNotificationsContent(controller, container);
|
Fn<void(Type)> showOther) {
|
||||||
|
SetupNotificationsContent(controller, container, std::move(showOther));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -1177,11 +1312,17 @@ rpl::producer<QString> Notifications::title() {
|
||||||
return tr::lng_settings_section_notify();
|
return tr::lng_settings_section_notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<Type> Notifications::sectionShowOther() {
|
||||||
|
return _showOther.events();
|
||||||
|
}
|
||||||
|
|
||||||
void Notifications::setupContent(
|
void Notifications::setupContent(
|
||||||
not_null<Window::SessionController*> controller) {
|
not_null<Window::SessionController*> controller) {
|
||||||
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
|
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
|
||||||
|
|
||||||
SetupNotifications(controller, content);
|
SetupNotifications(controller, content, [=](Type type) {
|
||||||
|
_showOther.fire_copy(type);
|
||||||
|
});
|
||||||
|
|
||||||
Ui::ResizeFitChild(this, content);
|
Ui::ResizeFitChild(this, content);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,13 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<QString> title() override;
|
[[nodiscard]] rpl::producer<QString> title() override;
|
||||||
|
|
||||||
|
rpl::producer<Type> sectionShowOther() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupContent(not_null<Window::SessionController*> controller);
|
void setupContent(not_null<Window::SessionController*> controller);
|
||||||
|
|
||||||
|
rpl::event_stream<Type> _showOther;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Settings
|
} // namespace Settings
|
||||||
|
|
633
Telegram/SourceFiles/settings/settings_notifications_type.cpp
Normal file
633
Telegram/SourceFiles/settings/settings_notifications_type.cpp
Normal file
|
@ -0,0 +1,633 @@
|
||||||
|
/*
|
||||||
|
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 "settings/settings_notifications_type.h"
|
||||||
|
|
||||||
|
#include "api/api_ringtones.h"
|
||||||
|
#include "apiwrap.h"
|
||||||
|
#include "base/unixtime.h"
|
||||||
|
#include "boxes/ringtones_box.h"
|
||||||
|
#include "boxes/peer_list_box.h"
|
||||||
|
#include "boxes/peer_list_controllers.h"
|
||||||
|
#include "data/notify/data_notify_settings.h"
|
||||||
|
#include "data/data_changes.h"
|
||||||
|
#include "data/data_peer.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "history/history.h"
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
#include "menu/menu_mute.h"
|
||||||
|
#include "ui/boxes/confirm_box.h"
|
||||||
|
#include "ui/widgets/buttons.h"
|
||||||
|
#include "ui/widgets/popup_menu.h"
|
||||||
|
#include "ui/wrap/slide_wrap.h"
|
||||||
|
#include "ui/wrap/vertical_layout.h"
|
||||||
|
#include "window/window_session_controller.h"
|
||||||
|
#include "styles/style_layers.h"
|
||||||
|
#include "styles/style_menu_icons.h"
|
||||||
|
#include "styles/style_settings.h"
|
||||||
|
|
||||||
|
namespace Settings {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using Notify = Data::DefaultNotify;
|
||||||
|
|
||||||
|
class AddExceptionBoxController final
|
||||||
|
: public ChatsListBoxController
|
||||||
|
, public base::has_weak_ptr {
|
||||||
|
public:
|
||||||
|
AddExceptionBoxController(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
Notify type,
|
||||||
|
Fn<void(not_null<PeerData*>)> done);
|
||||||
|
|
||||||
|
Main::Session &session() const override;
|
||||||
|
void rowClicked(not_null<PeerListRow*> row) override;
|
||||||
|
base::unique_qptr<Ui::PopupMenu> rowContextMenu(
|
||||||
|
QWidget *parent,
|
||||||
|
not_null<PeerListRow*> row) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void prepareViewHook() override;
|
||||||
|
std::unique_ptr<Row> createRow(not_null<History*> history) override;
|
||||||
|
|
||||||
|
const not_null<Main::Session*> _session;
|
||||||
|
const Notify _type;
|
||||||
|
const Fn<void(not_null<PeerData*>)> _done;
|
||||||
|
|
||||||
|
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||||
|
PeerData *_lastClickedPeer = nullptr;
|
||||||
|
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExceptionsController final : public PeerListController {
|
||||||
|
public:
|
||||||
|
ExceptionsController(
|
||||||
|
not_null<Window::SessionController*> window,
|
||||||
|
Notify type);
|
||||||
|
|
||||||
|
Main::Session &session() const override;
|
||||||
|
void prepare() override;
|
||||||
|
void rowClicked(not_null<PeerListRow*> row) override;
|
||||||
|
base::unique_qptr<Ui::PopupMenu> rowContextMenu(
|
||||||
|
QWidget *parent,
|
||||||
|
not_null<PeerListRow*> row) override;
|
||||||
|
void rowRightActionClicked(not_null<PeerListRow*> row) override;
|
||||||
|
void loadMoreRows() override;
|
||||||
|
|
||||||
|
void bringToTop(not_null<PeerData*> peer);
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<int> countValue() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void refreshRows();
|
||||||
|
bool appendRow(not_null<PeerData*> peer);
|
||||||
|
std::unique_ptr<PeerListRow> createRow(not_null<PeerData*> peer) const;
|
||||||
|
void refreshStatus(not_null<PeerListRow*> row) const;
|
||||||
|
|
||||||
|
void sort();
|
||||||
|
|
||||||
|
const not_null<Window::SessionController*> _window;
|
||||||
|
const Notify _type;
|
||||||
|
|
||||||
|
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||||
|
|
||||||
|
base::flat_map<not_null<PeerData*>, int> _topOrdered;
|
||||||
|
int _topOrder = 0;
|
||||||
|
|
||||||
|
rpl::variable<int> _count;
|
||||||
|
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
AddExceptionBoxController::AddExceptionBoxController(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
Notify type,
|
||||||
|
Fn<void(not_null<PeerData*>)> done)
|
||||||
|
: ChatsListBoxController(session)
|
||||||
|
, _session(session)
|
||||||
|
, _type(type)
|
||||||
|
, _done(std::move(done)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Main::Session &AddExceptionBoxController::session() const {
|
||||||
|
return *_session;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddExceptionBoxController::prepareViewHook() {
|
||||||
|
delegate()->peerListSetTitle(tr::lng_notification_exceptions_add());
|
||||||
|
|
||||||
|
_session->changes().peerUpdates(
|
||||||
|
Data::PeerUpdate::Flag::Notifications
|
||||||
|
) | rpl::filter([=](const Data::PeerUpdate &update) {
|
||||||
|
return update.peer == _lastClickedPeer;
|
||||||
|
}) | rpl::start_with_next([=] {
|
||||||
|
if (const auto onstack = _done) {
|
||||||
|
onstack(_lastClickedPeer);
|
||||||
|
}
|
||||||
|
}, _lifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddExceptionBoxController::rowClicked(not_null<PeerListRow*> row) {
|
||||||
|
delegate()->peerListShowRowMenu(row, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
base::unique_qptr<Ui::PopupMenu> AddExceptionBoxController::rowContextMenu(
|
||||||
|
QWidget *parent,
|
||||||
|
not_null<PeerListRow*> row) {
|
||||||
|
const auto peer = row->peer();
|
||||||
|
auto result = base::make_unique_q<Ui::PopupMenu>(
|
||||||
|
parent,
|
||||||
|
st::popupMenuWithIcons);
|
||||||
|
|
||||||
|
MuteMenu::FillMuteMenu(
|
||||||
|
result.get(),
|
||||||
|
peer->owner().history(peer),
|
||||||
|
delegate()->peerListUiShow());
|
||||||
|
|
||||||
|
// First clear _menu value, so that we don't check row positions yet.
|
||||||
|
base::take(_menu);
|
||||||
|
|
||||||
|
// Here unique_qptr is used like a shared pointer, where
|
||||||
|
// not the last destroyed pointer destroys the object, but the first.
|
||||||
|
_menu = base::unique_qptr<Ui::PopupMenu>(result.get());
|
||||||
|
_menu->setDestroyedCallback(crl::guard(this, [=] {
|
||||||
|
_lastClickedPeer = nullptr;
|
||||||
|
}));
|
||||||
|
_lastClickedPeer = peer;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto AddExceptionBoxController::createRow(not_null<History*> history)
|
||||||
|
-> std::unique_ptr<AddExceptionBoxController::Row> {
|
||||||
|
if (Data::DefaultNotifyType(history->peer) != _type
|
||||||
|
|| history->peer->isSelf()
|
||||||
|
|| history->peer->isRepliesChat()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return std::make_unique<Row>(history);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExceptionsController::ExceptionsController(
|
||||||
|
not_null<Window::SessionController*> window,
|
||||||
|
Notify type)
|
||||||
|
: _window(window)
|
||||||
|
, _type(type) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Main::Session &ExceptionsController::session() const {
|
||||||
|
return _window->session();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExceptionsController::prepare() {
|
||||||
|
refreshRows();
|
||||||
|
|
||||||
|
session().data().notifySettings().exceptionsUpdates(
|
||||||
|
) | rpl::filter(rpl::mappers::_1 == _type) | rpl::start_with_next([=] {
|
||||||
|
refreshRows();
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
|
session().changes().peerUpdates(
|
||||||
|
Data::PeerUpdate::Flag::Notifications
|
||||||
|
) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
|
||||||
|
const auto peer = update.peer;
|
||||||
|
if (const auto row = delegate()->peerListFindRow(peer->id.value)) {
|
||||||
|
if (peer->notify().muteUntil().has_value()) {
|
||||||
|
refreshStatus(row);
|
||||||
|
} else {
|
||||||
|
delegate()->peerListRemoveRow(row);
|
||||||
|
delegate()->peerListRefreshRows();
|
||||||
|
_count = delegate()->peerListFullRowsCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, _lifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExceptionsController::loadMoreRows() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExceptionsController::bringToTop(not_null<PeerData*> peer) {
|
||||||
|
_topOrdered[peer] = ++_topOrder;
|
||||||
|
if (delegate()->peerListFindRow(peer->id.value)) {
|
||||||
|
sort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<int> ExceptionsController::countValue() const {
|
||||||
|
return _count.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExceptionsController::rowClicked(not_null<PeerListRow*> row) {
|
||||||
|
delegate()->peerListShowRowMenu(row, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExceptionsController::rowRightActionClicked(
|
||||||
|
not_null<PeerListRow*> row) {
|
||||||
|
session().data().notifySettings().resetToDefault(row->peer());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExceptionsController::refreshRows() {
|
||||||
|
auto seen = base::flat_set<not_null<PeerData*>>();
|
||||||
|
const auto &list = session().data().notifySettings().exceptions(_type);
|
||||||
|
auto removed = false, added = false;
|
||||||
|
auto already = delegate()->peerListFullRowsCount();
|
||||||
|
seen.reserve(std::min(int(list.size()), already));
|
||||||
|
for (auto i = 0; i != already;) {
|
||||||
|
const auto row = delegate()->peerListRowAt(i);
|
||||||
|
if (list.contains(row->peer())) {
|
||||||
|
seen.emplace(row->peer());
|
||||||
|
++i;
|
||||||
|
} else {
|
||||||
|
delegate()->peerListRemoveRow(row);
|
||||||
|
--already;
|
||||||
|
removed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto &peer : list) {
|
||||||
|
if (!seen.contains(peer)) {
|
||||||
|
appendRow(peer);
|
||||||
|
added = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (added || removed) {
|
||||||
|
if (added) {
|
||||||
|
sort();
|
||||||
|
}
|
||||||
|
delegate()->peerListRefreshRows();
|
||||||
|
_count = delegate()->peerListFullRowsCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
base::unique_qptr<Ui::PopupMenu> ExceptionsController::rowContextMenu(
|
||||||
|
QWidget *parent,
|
||||||
|
not_null<PeerListRow*> row) {
|
||||||
|
const auto peer = row->peer();
|
||||||
|
auto result = base::make_unique_q<Ui::PopupMenu>(
|
||||||
|
parent,
|
||||||
|
st::popupMenuWithIcons);
|
||||||
|
|
||||||
|
result->addAction(
|
||||||
|
(peer->isUser()
|
||||||
|
? tr::lng_context_view_profile
|
||||||
|
: peer->isBroadcast()
|
||||||
|
? tr::lng_context_view_channel
|
||||||
|
: tr::lng_context_view_group)(tr::now),
|
||||||
|
crl::guard(_window, [window = _window.get(), peer] {
|
||||||
|
window->showPeerInfo(peer);
|
||||||
|
}),
|
||||||
|
(peer->isUser() ? &st::menuIconProfile : &st::menuIconInfo));
|
||||||
|
result->addSeparator();
|
||||||
|
|
||||||
|
MuteMenu::FillMuteMenu(
|
||||||
|
result.get(),
|
||||||
|
peer->owner().history(peer),
|
||||||
|
_window->uiShow());
|
||||||
|
|
||||||
|
// First clear _menu value, so that we don't check row positions yet.
|
||||||
|
base::take(_menu);
|
||||||
|
|
||||||
|
// Here unique_qptr is used like a shared pointer, where
|
||||||
|
// not the last destroyed pointer destroys the object, but the first.
|
||||||
|
_menu = base::unique_qptr<Ui::PopupMenu>(result.get());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExceptionsController::appendRow(not_null<PeerData*> peer) {
|
||||||
|
delegate()->peerListAppendRow(createRow(peer));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<PeerListRow> ExceptionsController::createRow(
|
||||||
|
not_null<PeerData*> peer) const {
|
||||||
|
auto row = std::make_unique<PeerListRowWithLink>(peer);
|
||||||
|
row->setActionLink(tr::lng_notification_exceptions_remove(tr::now));
|
||||||
|
refreshStatus(row.get());
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExceptionsController::refreshStatus(not_null<PeerListRow*> row) const {
|
||||||
|
const auto peer = row->peer();
|
||||||
|
const auto status = peer->owner().notifySettings().isMuted(peer)
|
||||||
|
? tr::lng_notification_exceptions_muted(tr::now)
|
||||||
|
: tr::lng_notification_exceptions_unmuted(tr::now);
|
||||||
|
row->setCustomStatus(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExceptionsController::sort() {
|
||||||
|
auto keys = base::flat_map<PeerListRowId, QString>();
|
||||||
|
keys.reserve(delegate()->peerListFullRowsCount());
|
||||||
|
const auto length = QString::number(_topOrder).size();
|
||||||
|
const auto key = [&](const PeerListRow &row) {
|
||||||
|
const auto id = row.id();
|
||||||
|
const auto i = keys.find(id);
|
||||||
|
if (i != end(keys)) {
|
||||||
|
return i->second;
|
||||||
|
}
|
||||||
|
const auto peer = row.peer();
|
||||||
|
const auto top = _topOrdered.find(peer);
|
||||||
|
if (top != end(_topOrdered)) {
|
||||||
|
const auto order = _topOrder - top->second;
|
||||||
|
return keys.emplace(
|
||||||
|
id,
|
||||||
|
u"0%1"_q.arg(order, length, 10, QChar('0'))).first->second;
|
||||||
|
}
|
||||||
|
const auto history = peer->owner().history(peer);
|
||||||
|
return keys.emplace(
|
||||||
|
id,
|
||||||
|
'1' + history->chatListNameSortKey()).first->second;
|
||||||
|
};
|
||||||
|
const auto predicate = [&](const PeerListRow &a, const PeerListRow &b) {
|
||||||
|
return (key(a).compare(key(b)) < 0);
|
||||||
|
};
|
||||||
|
delegate()->peerListSortRows(predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <Notify kType>
|
||||||
|
[[nodiscard]] Type Id() {
|
||||||
|
return &NotificationsTypeMetaImplementation<kType>::Meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<QString> Title(Notify type) {
|
||||||
|
switch (type) {
|
||||||
|
case Notify::User: return tr::lng_notification_title_private_chats();
|
||||||
|
case Notify::Group: return tr::lng_notification_title_groups();
|
||||||
|
case Notify::Broadcast: return tr::lng_notification_title_channels();
|
||||||
|
}
|
||||||
|
Unexpected("Type in Title.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupChecks(
|
||||||
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
not_null<Window::SessionController*> controller,
|
||||||
|
Notify type) {
|
||||||
|
AddSubsectionTitle(container, Title(type));
|
||||||
|
|
||||||
|
const auto session = &controller->session();
|
||||||
|
const auto settings = &session->data().notifySettings();
|
||||||
|
|
||||||
|
const auto enabled = container->add(
|
||||||
|
CreateButton(
|
||||||
|
container,
|
||||||
|
tr::lng_notification_enable(),
|
||||||
|
st::settingsButton,
|
||||||
|
{ &st::menuIconNotifications }));
|
||||||
|
enabled->toggleOn(
|
||||||
|
NotificationsEnabledForTypeValue(session, type),
|
||||||
|
true);
|
||||||
|
|
||||||
|
enabled->setAcceptBoth();
|
||||||
|
MuteMenu::SetupMuteMenu(
|
||||||
|
enabled,
|
||||||
|
enabled->clicks(
|
||||||
|
) | rpl::filter([=](Qt::MouseButton button) {
|
||||||
|
if (button == Qt::RightButton) {
|
||||||
|
return true;
|
||||||
|
} else if (settings->isMuted(type)) {
|
||||||
|
settings->defaultUpdate(type, { .unmute = true });
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}) | rpl::to_empty,
|
||||||
|
[=] { return MuteMenu::DefaultDescriptor(session, type); },
|
||||||
|
controller->uiShow());
|
||||||
|
|
||||||
|
const auto soundWrap = container->add(
|
||||||
|
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||||
|
container,
|
||||||
|
object_ptr<Ui::VerticalLayout>(container)));
|
||||||
|
soundWrap->toggleOn(enabled->toggledValue());
|
||||||
|
soundWrap->finishAnimating();
|
||||||
|
|
||||||
|
const auto soundInner = soundWrap->entity();
|
||||||
|
const auto soundValue = [=] {
|
||||||
|
const auto sound = settings->defaultSettings(type).sound();
|
||||||
|
return !sound || !sound->none;
|
||||||
|
};
|
||||||
|
const auto sound = soundInner->add(
|
||||||
|
CreateButton(
|
||||||
|
soundInner,
|
||||||
|
tr::lng_notification_sound(),
|
||||||
|
st::settingsButton,
|
||||||
|
{ &st::menuIconUnmute }));
|
||||||
|
sound->toggleOn(rpl::single(
|
||||||
|
soundValue()
|
||||||
|
) | rpl::then(settings->defaultUpdates(
|
||||||
|
type
|
||||||
|
) | rpl::map([=] { return soundValue(); })));
|
||||||
|
|
||||||
|
const auto toneWrap = soundInner->add(
|
||||||
|
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||||
|
container,
|
||||||
|
object_ptr<Ui::VerticalLayout>(container)));
|
||||||
|
toneWrap->toggleOn(sound->toggledValue());
|
||||||
|
toneWrap->finishAnimating();
|
||||||
|
|
||||||
|
const auto toneInner = toneWrap->entity();
|
||||||
|
const auto toneLabel = toneInner->lifetime(
|
||||||
|
).make_state<rpl::event_stream<QString>>();
|
||||||
|
const auto toneValue = [=] {
|
||||||
|
const auto sound = settings->defaultSettings(type).sound();
|
||||||
|
return sound.value_or(Data::NotifySound());
|
||||||
|
};
|
||||||
|
const auto label = [=] {
|
||||||
|
const auto now = toneValue();
|
||||||
|
return !now.id
|
||||||
|
? tr::lng_ringtones_box_default(tr::now)
|
||||||
|
: ExtractRingtoneName(session->data().document(now.id));
|
||||||
|
};
|
||||||
|
settings->defaultUpdates(
|
||||||
|
Notify::User
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
toneLabel->fire(label());
|
||||||
|
}, toneInner->lifetime());
|
||||||
|
session->api().ringtones().listUpdates(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
toneLabel->fire(label());
|
||||||
|
}, toneInner->lifetime());
|
||||||
|
|
||||||
|
const auto tone = AddButtonWithLabel(
|
||||||
|
toneInner,
|
||||||
|
tr::lng_notification_tone(),
|
||||||
|
toneLabel->events_starting_with(label()),
|
||||||
|
st::settingsButton,
|
||||||
|
{ &st::menuIconSoundOn });
|
||||||
|
|
||||||
|
enabled->toggledValue(
|
||||||
|
) | rpl::filter([=](bool value) {
|
||||||
|
return (value != NotificationsEnabledForType(session, type));
|
||||||
|
}) | rpl::start_with_next([=](bool value) {
|
||||||
|
settings->defaultUpdate(type, Data::MuteValue{
|
||||||
|
.unmute = value,
|
||||||
|
.forever = !value,
|
||||||
|
});
|
||||||
|
}, sound->lifetime());
|
||||||
|
|
||||||
|
sound->toggledValue(
|
||||||
|
) | rpl::filter([=](bool enabled) {
|
||||||
|
const auto sound = settings->defaultSettings(type).sound();
|
||||||
|
return (!sound || !sound->none) != enabled;
|
||||||
|
}) | rpl::start_with_next([=](bool enabled) {
|
||||||
|
const auto value = Data::NotifySound{ .none = !enabled };
|
||||||
|
settings->defaultUpdate(type, {}, {}, value);
|
||||||
|
}, sound->lifetime());
|
||||||
|
|
||||||
|
tone->setClickedCallback([=] {
|
||||||
|
controller->show(Box(RingtonesBox, session, toneValue(), [=](
|
||||||
|
Data::NotifySound sound) {
|
||||||
|
settings->defaultUpdate(type, {}, {}, sound);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupExceptions(
|
||||||
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
not_null<Window::SessionController*> window,
|
||||||
|
Notify type) {
|
||||||
|
const auto add = AddButton(
|
||||||
|
container,
|
||||||
|
tr::lng_notification_exceptions_add(),
|
||||||
|
st::settingsButtonActive,
|
||||||
|
{ &st::menuIconInviteSettings });
|
||||||
|
|
||||||
|
auto controller = std::make_unique<ExceptionsController>(window, type);
|
||||||
|
controller->setStyleOverrides(&st::settingsBlockedList);
|
||||||
|
const auto content = container->add(
|
||||||
|
object_ptr<PeerListContent>(container, controller.get()));
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
std::unique_ptr<ExceptionsController> controller;
|
||||||
|
std::unique_ptr<PeerListContentDelegateSimple> delegate;
|
||||||
|
};
|
||||||
|
const auto state = content->lifetime().make_state<State>();
|
||||||
|
state->controller = std::move(controller);
|
||||||
|
state->delegate = std::make_unique<PeerListContentDelegateSimple>();
|
||||||
|
|
||||||
|
state->delegate->setContent(content);
|
||||||
|
state->controller->setDelegate(state->delegate.get());
|
||||||
|
|
||||||
|
add->setClickedCallback([=] {
|
||||||
|
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
|
||||||
|
const auto done = [=](not_null<PeerData*> peer) {
|
||||||
|
state->controller->bringToTop(peer);
|
||||||
|
if (*box) {
|
||||||
|
(*box)->closeBox();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
auto controller = std::make_unique<AddExceptionBoxController>(
|
||||||
|
&window->session(),
|
||||||
|
type,
|
||||||
|
crl::guard(content, done));
|
||||||
|
auto initBox = [=](not_null<PeerListBox*> box) {
|
||||||
|
box->addButton(tr::lng_cancel(), [box] { box->closeBox(); });
|
||||||
|
};
|
||||||
|
*box = window->show(
|
||||||
|
Box<PeerListBox>(std::move(controller), std::move(initBox)));
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto wrap = container->add(
|
||||||
|
object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
|
||||||
|
container,
|
||||||
|
CreateButton(
|
||||||
|
container,
|
||||||
|
tr::lng_notification_exceptions_clear(),
|
||||||
|
st::settingsAttentionButtonWithIcon,
|
||||||
|
{ &st::menuIconDeleteAttention })));
|
||||||
|
wrap->entity()->setClickedCallback([=] {
|
||||||
|
const auto clear = [=](Fn<void()> close) {
|
||||||
|
window->session().data().notifySettings().clearExceptions(type);
|
||||||
|
close();
|
||||||
|
};
|
||||||
|
window->show(Ui::MakeConfirmBox({
|
||||||
|
.text = tr::lng_notification_exceptions_clear_sure(),
|
||||||
|
.confirmed = clear,
|
||||||
|
.confirmText = tr::lng_notification_exceptions_clear_button(),
|
||||||
|
.confirmStyle = &st::attentionBoxButton,
|
||||||
|
.title = tr::lng_notification_exceptions_clear(),
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
wrap->toggleOn(
|
||||||
|
state->controller->countValue() | rpl::map(rpl::mappers::_1 > 1),
|
||||||
|
anim::type::instant);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
Type NotificationsTypeId(Notify type) {
|
||||||
|
switch (type) {
|
||||||
|
case Notify::User: return Id<Notify::User>();
|
||||||
|
case Notify::Group: return Id<Notify::Group>();
|
||||||
|
case Notify::Broadcast: return Id<Notify::Broadcast>();
|
||||||
|
}
|
||||||
|
Unexpected("Type in NotificationTypeId.");
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationsType::NotificationsType(
|
||||||
|
QWidget *parent,
|
||||||
|
not_null<Window::SessionController*> controller,
|
||||||
|
Notify type)
|
||||||
|
: AbstractSection(parent)
|
||||||
|
, _type(type) {
|
||||||
|
setupContent(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<QString> NotificationsType::title() {
|
||||||
|
switch (_type) {
|
||||||
|
case Notify::User: return tr::lng_notification_private_chats();
|
||||||
|
case Notify::Group: return tr::lng_notification_groups();
|
||||||
|
case Notify::Broadcast: return tr::lng_notification_channels();
|
||||||
|
}
|
||||||
|
Unexpected("Type in NotificationsType.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Type NotificationsType::id() const {
|
||||||
|
return NotificationsTypeId(_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotificationsType::setupContent(
|
||||||
|
not_null<Window::SessionController*> controller) {
|
||||||
|
const auto container = Ui::CreateChild<Ui::VerticalLayout>(this);
|
||||||
|
|
||||||
|
AddSkip(container, st::settingsPrivacySkip);
|
||||||
|
SetupChecks(container, controller, _type);
|
||||||
|
|
||||||
|
AddSkip(container);
|
||||||
|
AddDivider(container);
|
||||||
|
AddSkip(container);
|
||||||
|
|
||||||
|
SetupExceptions(container, controller, _type);
|
||||||
|
|
||||||
|
Ui::ResizeFitChild(this, container);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NotificationsEnabledForType(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
Notify type) {
|
||||||
|
const auto settings = &session->data().notifySettings();
|
||||||
|
const auto until = settings->defaultSettings(type).muteUntil();
|
||||||
|
return until && (*until <= base::unixtime::now());
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<bool> NotificationsEnabledForTypeValue(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
Notify type) {
|
||||||
|
const auto settings = &session->data().notifySettings();
|
||||||
|
return rpl::single(
|
||||||
|
rpl::empty
|
||||||
|
) | rpl::then(
|
||||||
|
settings->defaultUpdates(type)
|
||||||
|
) | rpl::map([=] {
|
||||||
|
return NotificationsEnabledForType(session, type);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Settings
|
62
Telegram/SourceFiles/settings/settings_notifications_type.h
Normal file
62
Telegram/SourceFiles/settings/settings_notifications_type.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
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 "settings/settings_common.h"
|
||||||
|
#include "data/notify/data_notify_settings.h"
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
enum class DefaultNotify;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
|
namespace Settings {
|
||||||
|
|
||||||
|
class NotificationsType : public AbstractSection {
|
||||||
|
public:
|
||||||
|
NotificationsType(
|
||||||
|
QWidget *parent,
|
||||||
|
not_null<Window::SessionController*> controller,
|
||||||
|
Data::DefaultNotify type);
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<QString> title() override;
|
||||||
|
|
||||||
|
[[nodiscard]] Type id() const final override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setupContent(not_null<Window::SessionController*> controller);
|
||||||
|
|
||||||
|
Data::DefaultNotify _type;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template <Data::DefaultNotify kType>
|
||||||
|
struct NotificationsTypeMetaImplementation : SectionMeta {
|
||||||
|
object_ptr<AbstractSection> create(
|
||||||
|
not_null<QWidget*> parent,
|
||||||
|
not_null<Window::SessionController*> controller
|
||||||
|
) const final override {
|
||||||
|
return object_ptr<NotificationsType>(parent, controller, kType);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static not_null<SectionMeta*> Meta() {
|
||||||
|
static NotificationsTypeMetaImplementation result;
|
||||||
|
return &result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] Type NotificationsTypeId(Data::DefaultNotify type);
|
||||||
|
|
||||||
|
[[nodiscard]] bool NotificationsEnabledForType(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
Data::DefaultNotify type);
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<bool> NotificationsEnabledForTypeValue(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
Data::DefaultNotify type);
|
||||||
|
|
||||||
|
} // namespace Settings
|
|
@ -41,260 +41,6 @@ namespace {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
QByteArray ExpandInlineBytes(const QByteArray &bytes) {
|
|
||||||
if (bytes.size() < 3 || bytes[0] != '\x01') {
|
|
||||||
return QByteArray();
|
|
||||||
}
|
|
||||||
const char header[] = "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49"
|
|
||||||
"\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00\x43\x00\x28\x1c"
|
|
||||||
"\x1e\x23\x1e\x19\x28\x23\x21\x23\x2d\x2b\x28\x30\x3c\x64\x41\x3c\x37\x37"
|
|
||||||
"\x3c\x7b\x58\x5d\x49\x64\x91\x80\x99\x96\x8f\x80\x8c\x8a\xa0\xb4\xe6\xc3"
|
|
||||||
"\xa0\xaa\xda\xad\x8a\x8c\xc8\xff\xcb\xda\xee\xf5\xff\xff\xff\x9b\xc1\xff"
|
|
||||||
"\xff\xff\xfa\xff\xe6\xfd\xff\xf8\xff\xdb\x00\x43\x01\x2b\x2d\x2d\x3c\x35"
|
|
||||||
"\x3c\x76\x41\x41\x76\xf8\xa5\x8c\xa5\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
|
|
||||||
"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
|
|
||||||
"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
|
|
||||||
"\xf8\xf8\xf8\xf8\xf8\xff\xc0\x00\x11\x08\x00\x00\x00\x00\x03\x01\x22\x00"
|
|
||||||
"\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01"
|
|
||||||
"\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08"
|
|
||||||
"\x09\x0a\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05"
|
|
||||||
"\x04\x04\x00\x00\x01\x7d\x01\x02\x03\x00\x04\x11\x05\x12\x21\x31\x41\x06"
|
|
||||||
"\x13\x51\x61\x07\x22\x71\x14\x32\x81\x91\xa1\x08\x23\x42\xb1\xc1\x15\x52"
|
|
||||||
"\xd1\xf0\x24\x33\x62\x72\x82\x09\x0a\x16\x17\x18\x19\x1a\x25\x26\x27\x28"
|
|
||||||
"\x29\x2a\x34\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a\x53"
|
|
||||||
"\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74\x75"
|
|
||||||
"\x76\x77\x78\x79\x7a\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96"
|
|
||||||
"\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6"
|
|
||||||
"\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6"
|
|
||||||
"\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4"
|
|
||||||
"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xc4\x00\x1f\x01\x00\x03\x01\x01\x01\x01\x01"
|
|
||||||
"\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08"
|
|
||||||
"\x09\x0a\x0b\xff\xc4\x00\xb5\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05"
|
|
||||||
"\x04\x04\x00\x01\x02\x77\x00\x01\x02\x03\x11\x04\x05\x21\x31\x06\x12\x41"
|
|
||||||
"\x51\x07\x61\x71\x13\x22\x32\x81\x08\x14\x42\x91\xa1\xb1\xc1\x09\x23\x33"
|
|
||||||
"\x52\xf0\x15\x62\x72\xd1\x0a\x16\x24\x34\xe1\x25\xf1\x17\x18\x19\x1a\x26"
|
|
||||||
"\x27\x28\x29\x2a\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a"
|
|
||||||
"\x53\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74"
|
|
||||||
"\x75\x76\x77\x78\x79\x7a\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94"
|
|
||||||
"\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4"
|
|
||||||
"\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4"
|
|
||||||
"\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4"
|
|
||||||
"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00"
|
|
||||||
"\x3f\x00";
|
|
||||||
const char footer[] = "\xff\xd9";
|
|
||||||
auto real = QByteArray(header, sizeof(header) - 1);
|
|
||||||
real[164] = bytes[1];
|
|
||||||
real[166] = bytes[2];
|
|
||||||
return real
|
|
||||||
+ bytes.mid(3)
|
|
||||||
+ QByteArray::fromRawData(footer, sizeof(footer) - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
QImage FromInlineBytes(const QByteArray &bytes) {
|
|
||||||
return Read({ .content = ExpandInlineBytes(bytes) }).image;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thanks TDLib for code.
|
|
||||||
QByteArray ExpandPathInlineBytes(const QByteArray &bytes) {
|
|
||||||
auto result = QByteArray();
|
|
||||||
result.reserve(3 * (bytes.size() + 1));
|
|
||||||
result.append('M');
|
|
||||||
for (unsigned char c : bytes) {
|
|
||||||
if (c >= 128 + 64) {
|
|
||||||
result.append("AACAAAAHAAALMAAAQASTAVAAAZ"
|
|
||||||
"aacaaaahaaalmaaaqastava.az0123456789-,"[c - 128 - 64]);
|
|
||||||
} else {
|
|
||||||
if (c >= 128) {
|
|
||||||
result.append(',');
|
|
||||||
} else if (c >= 64) {
|
|
||||||
result.append('-');
|
|
||||||
}
|
|
||||||
//char buffer[3] = { 0 }; // Unavailable on macOS < 10.15.
|
|
||||||
//std::to_chars(buffer, buffer + 3, (c & 63));
|
|
||||||
//result.append(buffer);
|
|
||||||
result.append(QByteArray::number(c & 63));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.append('z');
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
QPainterPath PathFromInlineBytes(const QByteArray &bytes) {
|
|
||||||
if (bytes.isEmpty()) {
|
|
||||||
return QPainterPath();
|
|
||||||
}
|
|
||||||
const auto expanded = ExpandPathInlineBytes(bytes);
|
|
||||||
const auto path = expanded.data(); // Allows checking for '\0' by index.
|
|
||||||
auto position = 0;
|
|
||||||
|
|
||||||
const auto isAlpha = [](char c) {
|
|
||||||
c |= 0x20;
|
|
||||||
return 'a' <= c && c <= 'z';
|
|
||||||
};
|
|
||||||
const auto isDigit = [](char c) {
|
|
||||||
return '0' <= c && c <= '9';
|
|
||||||
};
|
|
||||||
const auto skipCommas = [&] {
|
|
||||||
while (path[position] == ',') {
|
|
||||||
++position;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const auto getNumber = [&] {
|
|
||||||
skipCommas();
|
|
||||||
auto sign = 1;
|
|
||||||
if (path[position] == '-') {
|
|
||||||
sign = -1;
|
|
||||||
++position;
|
|
||||||
}
|
|
||||||
double res = 0;
|
|
||||||
while (isDigit(path[position])) {
|
|
||||||
res = res * 10 + path[position++] - '0';
|
|
||||||
}
|
|
||||||
if (path[position] == '.') {
|
|
||||||
++position;
|
|
||||||
double mul = 0.1;
|
|
||||||
while (isDigit(path[position])) {
|
|
||||||
res += (path[position] - '0') * mul;
|
|
||||||
mul *= 0.1;
|
|
||||||
++position;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sign * res;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto result = QPainterPath();
|
|
||||||
auto x = 0.;
|
|
||||||
auto y = 0.;
|
|
||||||
while (path[position] != '\0') {
|
|
||||||
skipCommas();
|
|
||||||
if (path[position] == '\0') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (path[position] == 'm' || path[position] == 'M') {
|
|
||||||
auto command = path[position++];
|
|
||||||
do {
|
|
||||||
if (command == 'm') {
|
|
||||||
x += getNumber();
|
|
||||||
y += getNumber();
|
|
||||||
} else {
|
|
||||||
x = getNumber();
|
|
||||||
y = getNumber();
|
|
||||||
}
|
|
||||||
skipCommas();
|
|
||||||
} while (path[position] != '\0' && !isAlpha(path[position]));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto xStart = x;
|
|
||||||
auto yStart = y;
|
|
||||||
result.moveTo(xStart, yStart);
|
|
||||||
auto haveLastEndControlPoint = false;
|
|
||||||
auto xLastEndControlPoint = 0.;
|
|
||||||
auto yLastEndControlPoint = 0.;
|
|
||||||
auto isClosed = false;
|
|
||||||
auto command = '-';
|
|
||||||
while (!isClosed) {
|
|
||||||
skipCommas();
|
|
||||||
if (path[position] == '\0') {
|
|
||||||
LOG(("SVG Error: Receive unclosed path: %1"
|
|
||||||
).arg(QString::fromLatin1(path)));
|
|
||||||
return QPainterPath();
|
|
||||||
}
|
|
||||||
if (isAlpha(path[position])) {
|
|
||||||
command = path[position++];
|
|
||||||
}
|
|
||||||
switch (command) {
|
|
||||||
case 'l':
|
|
||||||
case 'L':
|
|
||||||
case 'h':
|
|
||||||
case 'H':
|
|
||||||
case 'v':
|
|
||||||
case 'V':
|
|
||||||
if (command == 'l' || command == 'h') {
|
|
||||||
x += getNumber();
|
|
||||||
} else if (command == 'L' || command == 'H') {
|
|
||||||
x = getNumber();
|
|
||||||
}
|
|
||||||
if (command == 'l' || command == 'v') {
|
|
||||||
y += getNumber();
|
|
||||||
} else if (command == 'L' || command == 'V') {
|
|
||||||
y = getNumber();
|
|
||||||
}
|
|
||||||
result.lineTo(x, y);
|
|
||||||
haveLastEndControlPoint = false;
|
|
||||||
break;
|
|
||||||
case 'C':
|
|
||||||
case 'c':
|
|
||||||
case 'S':
|
|
||||||
case 's': {
|
|
||||||
auto xStartControlPoint = 0.;
|
|
||||||
auto yStartControlPoint = 0.;
|
|
||||||
if (command == 'S' || command == 's') {
|
|
||||||
if (haveLastEndControlPoint) {
|
|
||||||
xStartControlPoint = 2 * x - xLastEndControlPoint;
|
|
||||||
yStartControlPoint = 2 * y - yLastEndControlPoint;
|
|
||||||
} else {
|
|
||||||
xStartControlPoint = x;
|
|
||||||
yStartControlPoint = y;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
xStartControlPoint = getNumber();
|
|
||||||
yStartControlPoint = getNumber();
|
|
||||||
if (command == 'c') {
|
|
||||||
xStartControlPoint += x;
|
|
||||||
yStartControlPoint += y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xLastEndControlPoint = getNumber();
|
|
||||||
yLastEndControlPoint = getNumber();
|
|
||||||
if (command == 'c' || command == 's') {
|
|
||||||
xLastEndControlPoint += x;
|
|
||||||
yLastEndControlPoint += y;
|
|
||||||
}
|
|
||||||
haveLastEndControlPoint = true;
|
|
||||||
|
|
||||||
if (command == 'c' || command == 's') {
|
|
||||||
x += getNumber();
|
|
||||||
y += getNumber();
|
|
||||||
} else {
|
|
||||||
x = getNumber();
|
|
||||||
y = getNumber();
|
|
||||||
}
|
|
||||||
result.cubicTo(
|
|
||||||
xStartControlPoint,
|
|
||||||
yStartControlPoint,
|
|
||||||
xLastEndControlPoint,
|
|
||||||
yLastEndControlPoint,
|
|
||||||
x,
|
|
||||||
y);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'm':
|
|
||||||
case 'M':
|
|
||||||
--position;
|
|
||||||
[[fallthrough]];
|
|
||||||
case 'z':
|
|
||||||
case 'Z':
|
|
||||||
if (x != xStart || y != yStart) {
|
|
||||||
x = xStart;
|
|
||||||
y = yStart;
|
|
||||||
result.lineTo(x, y);
|
|
||||||
}
|
|
||||||
isClosed = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG(("SVG Error: Receive invalid command %1 at pos %2: %3"
|
|
||||||
).arg(command
|
|
||||||
).arg(position
|
|
||||||
).arg(QString::fromLatin1(path)));
|
|
||||||
return QPainterPath();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Images
|
} // namespace Images
|
||||||
|
|
||||||
Image::Image(const QString &path)
|
Image::Image(const QString &path)
|
||||||
|
|
|
@ -11,14 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
class QPainterPath;
|
class QPainterPath;
|
||||||
|
|
||||||
namespace Images {
|
|
||||||
|
|
||||||
[[nodiscard]] QByteArray ExpandInlineBytes(const QByteArray &bytes);
|
|
||||||
[[nodiscard]] QImage FromInlineBytes(const QByteArray &bytes);
|
|
||||||
[[nodiscard]] QPainterPath PathFromInlineBytes(const QByteArray &bytes);
|
|
||||||
|
|
||||||
} // namespace Images
|
|
||||||
|
|
||||||
class Image final {
|
class Image final {
|
||||||
public:
|
public:
|
||||||
explicit Image(const QString &path);
|
explicit Image(const QString &path);
|
||||||
|
|
|
@ -173,6 +173,7 @@ menuIconReportAttention: icon {{ "menu/report", menuIconAttentionColor }};
|
||||||
menuIconRestoreAttention: icon {{ "menu/restore", menuIconAttentionColor }};
|
menuIconRestoreAttention: icon {{ "menu/restore", menuIconAttentionColor }};
|
||||||
|
|
||||||
menuIconBlockSettings: icon {{ "menu/block", windowBgActive }};
|
menuIconBlockSettings: icon {{ "menu/block", windowBgActive }};
|
||||||
|
menuIconInviteSettings: icon {{ "menu/invite", windowBgActive }};
|
||||||
|
|
||||||
playerSpeedSlow: icon {{ "player/speed/audiospeed_menu_0.5", menuIconColor }};
|
playerSpeedSlow: icon {{ "player/speed/audiospeed_menu_0.5", menuIconColor }};
|
||||||
playerSpeedSlowActive: icon {{ "player/speed/audiospeed_menu_0.5", mediaPlayerActiveFg }};
|
playerSpeedSlowActive: icon {{ "player/speed/audiospeed_menu_0.5", mediaPlayerActiveFg }};
|
||||||
|
|
|
@ -66,7 +66,15 @@ constexpr auto kSystemAlertDuration = crl::time(0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TextWithPermanentSpoiler(const TextWithEntities &textWithEntities) {
|
[[nodiscard]] QString TextWithForwardedChar(
|
||||||
|
const QString &text,
|
||||||
|
bool forwarded) {
|
||||||
|
static const auto result = QString::fromUtf8("\xE2\x9E\xA1\xEF\xB8\x8F");
|
||||||
|
return forwarded ? result + text : text;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] QString TextWithPermanentSpoiler(
|
||||||
|
const TextWithEntities &textWithEntities) {
|
||||||
auto text = textWithEntities.text;
|
auto text = textWithEntities.text;
|
||||||
for (const auto &e : textWithEntities.entities) {
|
for (const auto &e : textWithEntities.entities) {
|
||||||
if (e.type() == EntityType::Spoiler) {
|
if (e.type() == EntityType::Spoiler) {
|
||||||
|
@ -1175,9 +1183,11 @@ void NativeManager::doShowNotification(NotificationFields &&fields) {
|
||||||
? tr::lng_forward_messages(tr::now, lt_count, fields.forwardedCount)
|
? tr::lng_forward_messages(tr::now, lt_count, fields.forwardedCount)
|
||||||
: item->groupId()
|
: item->groupId()
|
||||||
? tr::lng_in_dlg_album(tr::now)
|
? tr::lng_in_dlg_album(tr::now)
|
||||||
: TextWithPermanentSpoiler(item->notificationText({
|
: TextWithForwardedChar(
|
||||||
.spoilerLoginCode = options.spoilerLoginCode,
|
TextWithPermanentSpoiler(item->notificationText({
|
||||||
}));
|
.spoilerLoginCode = options.spoilerLoginCode,
|
||||||
|
})),
|
||||||
|
(fields.forwardedCount == 1));
|
||||||
|
|
||||||
// #TODO optimize
|
// #TODO optimize
|
||||||
auto userpicView = item->history()->peer->createUserpicView();
|
auto userpicView = item->history()->peer->createUserpicView();
|
||||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "window/themes/window_theme_preview.h"
|
#include "window/themes/window_theme_preview.h"
|
||||||
|
|
||||||
|
#include "dialogs/dialogs_three_state_icon.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "platform/platform_window_title.h"
|
#include "platform/platform_window_title.h"
|
||||||
#include "ui/text/text_options.h"
|
#include "ui/text/text_options.h"
|
||||||
|
@ -695,9 +696,15 @@ void Generator::paintRow(const Row &row) {
|
||||||
|
|
||||||
auto chatTypeIcon = ([&row]() -> const style::icon * {
|
auto chatTypeIcon = ([&row]() -> const style::icon * {
|
||||||
if (row.type == Row::Type::Group) {
|
if (row.type == Row::Type::Group) {
|
||||||
return &(row.active ? st::dialogsChatIconActive : (row.selected ? st::dialogsChatIconOver : st::dialogsChatIcon));
|
return &Dialogs::ThreeStateIcon(
|
||||||
|
st::dialogsChatIcon,
|
||||||
|
row.active,
|
||||||
|
row.selected);
|
||||||
} else if (row.type == Row::Type::Channel) {
|
} else if (row.type == Row::Type::Channel) {
|
||||||
return &(row.active ? st::dialogsChannelIconActive : (row.selected ? st::dialogsChannelIconOver : st::dialogsChannelIcon));
|
return &Dialogs::ThreeStateIcon(
|
||||||
|
st::dialogsChannelIcon,
|
||||||
|
row.active,
|
||||||
|
row.selected);
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
})();
|
})();
|
||||||
|
@ -750,7 +757,10 @@ void Generator::paintRow(const Row &row) {
|
||||||
_p->setPen(row.active ? st::dialogsUnreadFgActive[_palette] : (row.selected ? st::dialogsUnreadFgOver[_palette] : st::dialogsUnreadFg[_palette]));
|
_p->setPen(row.active ? st::dialogsUnreadFgActive[_palette] : (row.selected ? st::dialogsUnreadFgOver[_palette] : st::dialogsUnreadFg[_palette]));
|
||||||
_p->drawText(unreadRectLeft + (unreadRectWidth - unreadWidth) / 2, unreadRectTop + textTop + st::dialogsUnreadFont->ascent, counter);
|
_p->drawText(unreadRectLeft + (unreadRectWidth - unreadWidth) / 2, unreadRectTop + textTop + st::dialogsUnreadFont->ascent, counter);
|
||||||
} else if (row.pinned) {
|
} else if (row.pinned) {
|
||||||
auto icon = (row.active ? st::dialogsPinnedIconActive[_palette] : (row.selected ? st::dialogsPinnedIconOver[_palette] : st::dialogsPinnedIcon[_palette]));
|
auto icon = Dialogs::ThreeStateIcon(
|
||||||
|
st::dialogsPinnedIcon,
|
||||||
|
row.active,
|
||||||
|
row.selected)[_palette];
|
||||||
icon.paint(*_p, x + fullWidth - st.padding.right() - icon.width(), texttop, fullWidth);
|
icon.paint(*_p, x + fullWidth - st.padding.right() - icon.width(), texttop, fullWidth);
|
||||||
availableWidth -= icon.width() + st::dialogsUnreadPadding;
|
availableWidth -= icon.width() + st::dialogsUnreadPadding;
|
||||||
}
|
}
|
||||||
|
@ -763,9 +773,15 @@ void Generator::paintRow(const Row &row) {
|
||||||
|
|
||||||
auto sendStateIcon = ([&row]() -> const style::icon* {
|
auto sendStateIcon = ([&row]() -> const style::icon* {
|
||||||
if (row.status == Status::Sent) {
|
if (row.status == Status::Sent) {
|
||||||
return &(row.active ? st::dialogsSentIconActive : (row.selected ? st::dialogsSentIconOver : st::dialogsSentIcon));
|
return &Dialogs::ThreeStateIcon(
|
||||||
|
st::dialogsSentIcon,
|
||||||
|
row.active,
|
||||||
|
row.selected);
|
||||||
} else if (row.status == Status::Received) {
|
} else if (row.status == Status::Received) {
|
||||||
return &(row.active ? st::dialogsReceivedIconActive : (row.selected ? st::dialogsReceivedIconOver : st::dialogsReceivedIcon));
|
return &Dialogs::ThreeStateIcon(
|
||||||
|
st::dialogsReceivedIcon,
|
||||||
|
row.active,
|
||||||
|
row.selected);
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -10,11 +10,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "boxes/add_contact_box.h"
|
#include "boxes/add_contact_box.h"
|
||||||
#include "boxes/peers/add_bot_to_chat_box.h"
|
#include "boxes/peers/add_bot_to_chat_box.h"
|
||||||
#include "boxes/peers/edit_peer_info_box.h"
|
#include "boxes/peers/edit_peer_info_box.h"
|
||||||
#include "boxes/peer_list_controllers.h"
|
|
||||||
#include "boxes/delete_messages_box.h"
|
#include "boxes/delete_messages_box.h"
|
||||||
#include "window/window_adaptive.h"
|
#include "window/window_adaptive.h"
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
#include "window/main_window.h"
|
|
||||||
#include "window/window_filters_menu.h"
|
#include "window/window_filters_menu.h"
|
||||||
#include "info/info_memento.h"
|
#include "info/info_memento.h"
|
||||||
#include "info/info_controller.h"
|
#include "info/info_controller.h"
|
||||||
|
@ -37,7 +35,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
#include "data/data_document_media.h"
|
#include "data/data_document_media.h"
|
||||||
#include "data/data_document_resolver.h"
|
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
#include "data/data_group_call.h"
|
#include "data/data_group_call.h"
|
||||||
#include "data/data_forum.h"
|
#include "data/data_forum.h"
|
||||||
|
@ -54,13 +51,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "core/core_settings.h"
|
#include "core/core_settings.h"
|
||||||
#include "core/click_handler_types.h"
|
#include "core/click_handler_types.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
#include "base/random.h"
|
|
||||||
#include "ui/layers/generic_box.h"
|
#include "ui/layers/generic_box.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/text/format_values.h" // Ui::FormatPhone.
|
#include "ui/text/format_values.h" // Ui::FormatPhone.
|
||||||
#include "ui/delayed_activation.h"
|
#include "ui/delayed_activation.h"
|
||||||
#include "ui/chat/attach/attach_bot_webview.h"
|
#include "ui/chat/attach/attach_bot_webview.h"
|
||||||
#include "ui/chat/message_bubble.h"
|
|
||||||
#include "ui/chat/chat_style.h"
|
#include "ui/chat/chat_style.h"
|
||||||
#include "ui/chat/chat_theme.h"
|
#include "ui/chat/chat_theme.h"
|
||||||
#include "ui/effects/message_sending_animation_controller.h"
|
#include "ui/effects/message_sending_animation_controller.h"
|
||||||
|
|
|
@ -16,7 +16,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_chat_participant_status.h"
|
#include "data/data_chat_participant_status.h"
|
||||||
#include "dialogs/dialogs_key.h"
|
#include "dialogs/dialogs_key.h"
|
||||||
#include "ui/layers/layer_widget.h"
|
#include "ui/layers/layer_widget.h"
|
||||||
#include "ui/layers/show.h"
|
|
||||||
#include "settings/settings_type.h"
|
#include "settings/settings_type.h"
|
||||||
#include "window/window_adaptive.h"
|
#include "window/window_adaptive.h"
|
||||||
#include "mtproto/sender.h"
|
#include "mtproto/sender.h"
|
||||||
|
@ -89,7 +88,6 @@ namespace Window {
|
||||||
using GifPauseReason = ChatHelpers::PauseReason;
|
using GifPauseReason = ChatHelpers::PauseReason;
|
||||||
using GifPauseReasons = ChatHelpers::PauseReasons;
|
using GifPauseReasons = ChatHelpers::PauseReasons;
|
||||||
|
|
||||||
class MainWindow;
|
|
||||||
class SectionMemento;
|
class SectionMemento;
|
||||||
class Controller;
|
class Controller;
|
||||||
class FiltersMenu;
|
class FiltersMenu;
|
||||||
|
|
2
Telegram/ThirdParty/kimageformats
vendored
2
Telegram/ThirdParty/kimageformats
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 63056c52f91aa4a34bb0637f1723848dc25a5f87
|
Subproject commit b2b677b8a5e4c3cf34790eb990218217bf867c18
|
|
@ -59,7 +59,7 @@ FROM builder AS patches
|
||||||
RUN git init patches \
|
RUN git init patches \
|
||||||
&& cd patches \
|
&& cd patches \
|
||||||
&& git remote add origin {{ GIT }}/desktop-app/patches.git \
|
&& git remote add origin {{ GIT }}/desktop-app/patches.git \
|
||||||
&& git fetch --depth=1 origin dbd8a5b00f5a0e7e09b11878b9e46afeb4b593c0 \
|
&& git fetch --depth=1 origin 2cf975200b9baac520b9b298776e0a20b24e397a \
|
||||||
&& git reset --hard FETCH_HEAD \
|
&& git reset --hard FETCH_HEAD \
|
||||||
&& rm -rf .git
|
&& rm -rf .git
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
AppVersion 4009003
|
AppVersion 4009004
|
||||||
AppVersionStrMajor 4.9
|
AppVersionStrMajor 4.9
|
||||||
AppVersionStrSmall 4.9.3
|
AppVersionStrSmall 4.9.4
|
||||||
AppVersionStr 4.9.3
|
AppVersionStr 4.9.4
|
||||||
BetaChannel 0
|
BetaChannel 0
|
||||||
AlphaVersion 0
|
AlphaVersion 0
|
||||||
AppVersionOriginal 4.9.3
|
AppVersionOriginal 4.9.4
|
||||||
|
|
|
@ -73,6 +73,7 @@ PRIVATE
|
||||||
|
|
||||||
data/data_subscription_option.h
|
data/data_subscription_option.h
|
||||||
|
|
||||||
|
dialogs/dialogs_three_state_icon.h
|
||||||
dialogs/ui/dialogs_stories_list.cpp
|
dialogs/ui/dialogs_stories_list.cpp
|
||||||
dialogs/ui/dialogs_stories_list.h
|
dialogs/ui/dialogs_stories_list.h
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 0919789bf555c651e32bdbcabc397dbacb6a2545
|
Subproject commit 764b55db1a4a8b9d8ba966d7f0fa46c9f384737e
|
|
@ -1 +1 @@
|
||||||
Subproject commit b94ec0107b03f13c28f336642411c6df21107a63
|
Subproject commit c989a9f41e5bd8268587af2256efa89327cb3ae0
|
|
@ -1 +1 @@
|
||||||
Subproject commit 5850566934f2f6cae56646c36cb95f85c8a9c752
|
Subproject commit 5b9092bcb27a207fed3cb2155bb98db46d7cedfa
|
|
@ -1,3 +1,9 @@
|
||||||
|
4.9.4 (30.08.23)
|
||||||
|
|
||||||
|
- Default private chats / groups / channels notification settings.
|
||||||
|
- Forwarded / reply-to-a-story icon in chats list message preview.
|
||||||
|
- Bug fixes and other minor improvements.
|
||||||
|
|
||||||
4.9.3 (22.08.23)
|
4.9.3 (22.08.23)
|
||||||
|
|
||||||
- Fix audio output on macOS.
|
- Fix audio output on macOS.
|
||||||
|
|
Loading…
Add table
Reference in a new issue