diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index f83c3e3b1..81651cec4 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -1314,6 +1314,8 @@ PRIVATE
settings/settings_main.h
settings/settings_notifications.cpp
settings/settings_notifications.h
+ settings/settings_notifications_type.cpp
+ settings/settings_notifications_type.h
settings/settings_power_saving.cpp
settings/settings_power_saving.h
settings/settings_premium.cpp
diff --git a/Telegram/Resources/icons/mini_forward.png b/Telegram/Resources/icons/mini_forward.png
new file mode 100644
index 000000000..0b12af15d
Binary files /dev/null and b/Telegram/Resources/icons/mini_forward.png differ
diff --git a/Telegram/Resources/icons/mini_forward@2x.png b/Telegram/Resources/icons/mini_forward@2x.png
new file mode 100644
index 000000000..c8194ef00
Binary files /dev/null and b/Telegram/Resources/icons/mini_forward@2x.png differ
diff --git a/Telegram/Resources/icons/mini_forward@3x.png b/Telegram/Resources/icons/mini_forward@3x.png
new file mode 100644
index 000000000..19cac4700
Binary files /dev/null and b/Telegram/Resources/icons/mini_forward@3x.png differ
diff --git a/Telegram/Resources/icons/mini_reply_story.png b/Telegram/Resources/icons/mini_reply_story.png
new file mode 100644
index 000000000..bfcbc0c48
Binary files /dev/null and b/Telegram/Resources/icons/mini_reply_story.png differ
diff --git a/Telegram/Resources/icons/mini_reply_story@2x.png b/Telegram/Resources/icons/mini_reply_story@2x.png
new file mode 100644
index 000000000..a3c75e8bf
Binary files /dev/null and b/Telegram/Resources/icons/mini_reply_story@2x.png differ
diff --git a/Telegram/Resources/icons/mini_reply_story@3x.png b/Telegram/Resources/icons/mini_reply_story@3x.png
new file mode 100644
index 000000000..471cfa57d
Binary files /dev/null and b/Telegram/Resources/icons/mini_reply_story@3x.png differ
diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 8f3ca7a71..626e9ae5b 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -451,6 +451,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_show_from" = "Show notifications from";
"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_global" = "Global settings";
"lng_settings_notify_title" = "Notifications for chats";
"lng_settings_desktop_notify" = "Desktop 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_notifications_position" = "Location on the screen";
"lng_settings_notifications_count" = "Notifications count";
-"lng_settings_sound_notify" = "Play sound";
-"lng_settings_sound_notify_off" = "Off";
+"lng_settings_sound_allowed" = "Allow sound";
"lng_settings_alert_windows" = "Flash the taskbar icon";
"lng_settings_alert_mac" = "Bounce the dock icon";
"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_sample" = "This is a sample notification";
"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_notext" = "{reaction} to your message";
diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml
index 15007bceb..3b5ae67a8 100644
--- a/Telegram/Resources/uwp/AppX/AppxManifest.xml
+++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml
@@ -10,7 +10,7 @@
+ Version="4.9.4.0" />
Telegram Desktop
Telegram Messenger LLP
diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc
index f13710dae..f0efceda4 100644
--- a/Telegram/Resources/winrc/Telegram.rc
+++ b/Telegram/Resources/winrc/Telegram.rc
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 4,9,3,0
- PRODUCTVERSION 4,9,3,0
+ FILEVERSION 4,9,4,0
+ PRODUCTVERSION 4,9,4,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -62,10 +62,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Radolyn Labs"
VALUE "FileDescription", "AyuGram Desktop"
- VALUE "FileVersion", "4.9.3.0"
+ VALUE "FileVersion", "4.9.4.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2023"
VALUE "ProductName", "AyuGram Desktop"
- VALUE "ProductVersion", "4.9.3.0"
+ VALUE "ProductVersion", "4.9.4.0"
END
END
BLOCK "VarFileInfo"
diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc
index 197deffe1..5d8768e00 100644
--- a/Telegram/Resources/winrc/Updater.rc
+++ b/Telegram/Resources/winrc/Updater.rc
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 4,9,3,0
- PRODUCTVERSION 4,9,3,0
+ FILEVERSION 4,9,4,0
+ PRODUCTVERSION 4,9,4,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -53,10 +53,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Radolyn Labs"
VALUE "FileDescription", "AyuGram Desktop Updater"
- VALUE "FileVersion", "4.9.3.0"
+ VALUE "FileVersion", "4.9.4.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2023"
VALUE "ProductName", "AyuGram Desktop"
- VALUE "ProductVersion", "4.9.3.0"
+ VALUE "ProductVersion", "4.9.4.0"
END
END
BLOCK "VarFileInfo"
diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index fec04ea1d..4c551d0e9 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -1889,17 +1889,8 @@ void ApiWrap::sendNotifySettingsUpdates() {
}
const auto &settings = session().data().notifySettings();
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(
- input,
+ Data::DefaultNotifyToMTP(type),
settings.defaultSettings(type).serialize()
)).afterDelay(kSmallDelayMs).send();
}
diff --git a/Telegram/SourceFiles/boxes/choose_filter_box.cpp b/Telegram/SourceFiles/boxes/choose_filter_box.cpp
index 181dccb1e..12d58b538 100644
--- a/Telegram/SourceFiles/boxes/choose_filter_box.cpp
+++ b/Telegram/SourceFiles/boxes/choose_filter_box.cpp
@@ -15,14 +15,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
-#include "ui/filter_icons.h"
#include "ui/text/text_utilities.h" // Ui::Text::Bold
#include "ui/widgets/buttons.h"
#include "ui/widgets/popup_menu.h"
#include "window/window_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
namespace {
@@ -32,16 +29,30 @@ Data::ChatFilter ChangedFilter(
not_null history,
bool add) {
auto always = base::duplicate(filter.always());
- if (add) {
- always.insert(history);
- } else {
- always.remove(history);
- }
auto never = base::duplicate(filter.never());
if (add) {
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 {
- never.insert(history);
+ const auto alwaysIt = always.find(history);
+ if (alwaysIt != end(always)) {
+ always.erase(alwaysIt);
+ } else {
+ never.insert(history);
+ }
}
return Data::ChatFilter(
filter.id(),
diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp
index 90a989ce7..4a2b53c5f 100644
--- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp
+++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp
@@ -622,9 +622,10 @@ void EditCaptionBox::setupDragArea() {
};
// Avoid both drag areas appearing at one time.
auto computeState = [=](const QMimeData *data) {
+ using DragState = Storage::MimeDataState;
const auto state = Storage::ComputeMimeDataState(data);
- return (state == Storage::MimeDataState::PhotoFiles)
- ? Storage::MimeDataState::Image
+ return (state == DragState::PhotoFiles || state == DragState::Image)
+ ? (_asFile ? DragState::Files : DragState::Image)
: state;
};
const auto areas = DragArea::SetupDragAreaToContainer(
diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp
index 6f27553d0..28286f377 100644
--- a/Telegram/SourceFiles/boxes/peer_list_box.cpp
+++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/peer_list_box.h"
+#include "history/history.h" // chatListNameSortKey.
#include "main/session/session_show.h"
#include "main/main_session.h"
#include "mainwidget.h"
@@ -396,6 +397,27 @@ void PeerListController::setSearchNoResultsText(const QString &text) {
}
}
+void PeerListController::sortByName() {
+ auto keys = base::flat_map();
+ 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 PeerListController::rowContextMenu(
QWidget *parent,
not_null row) {
@@ -741,8 +763,8 @@ int PeerListRow::paintNameIconGetWidth(
? st::dialogsVerifiedIconOver
: st::dialogsVerifiedIcon),
.premium = &(selected
- ? st::dialogsPremiumIconOver
- : st::dialogsPremiumIcon),
+ ? st::dialogsPremiumIcon.over
+ : st::dialogsPremiumIcon.icon),
.scam = &(selected ? st::dialogsScamFgOver : st::dialogsScamFg),
.premiumFg = &(selected
? st::dialogsVerifiedIconBgOver
diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h
index ac8ab554b..837bb6e2c 100644
--- a/Telegram/SourceFiles/boxes/peer_list_box.h
+++ b/Telegram/SourceFiles/boxes/peer_list_box.h
@@ -560,6 +560,8 @@ protected:
delegate()->peerListSetSearchNoResults(std::move(noResults));
}
+ void sortByName();
+
private:
PeerListDelegate *_delegate = nullptr;
std::unique_ptr _searchController = nullptr;
diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp
index 335531fc9..874e61e23 100644
--- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp
+++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp
@@ -594,25 +594,6 @@ void ContactsBoxController::sort() {
}
}
-void ContactsBoxController::sortByName() {
- auto keys = base::flat_map();
- 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() {
const auto now = base::unixtime::now();
const auto key = [&](const PeerListRow &row) {
diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.h b/Telegram/SourceFiles/boxes/peer_list_controllers.h
index 0ba108f01..35b4e0f15 100644
--- a/Telegram/SourceFiles/boxes/peer_list_controllers.h
+++ b/Telegram/SourceFiles/boxes/peer_list_controllers.h
@@ -192,7 +192,6 @@ protected:
private:
void sort();
- void sortByName();
void sortByOnline();
void rebuildRows();
void checkForEmptyRows();
diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp
index 4c33b38fc..97a86be3b 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp
@@ -319,16 +319,15 @@ not_null AddInnerToggle(
button->geometryValue(
) | rpl::start_with_next([=](const QRect &r) {
const auto w = st::rightsButtonToggleWidth;
- constexpr auto kLineWidth = int(1);
toggleButton->setGeometry(
r.x() + r.width() - w,
r.y(),
w,
r.height());
separator->setGeometry(
- toggleButton->x() - kLineWidth,
+ toggleButton->x() - st::lineWidth,
r.y() + (r.height() - separatorHeight) / 2,
- kLineWidth,
+ st::lineWidth,
separatorHeight);
}, toggleButton->lifetime());
diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp
index 7313afd21..50f8886d9 100644
--- a/Telegram/SourceFiles/boxes/send_files_box.cpp
+++ b/Telegram/SourceFiles/boxes/send_files_box.cpp
@@ -28,8 +28,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/call_delayed.h"
#include "boxes/premium_limits_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/widgets/checkbox.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_single_file_preview.h"
#include "ui/chat/attach/attach_single_media_preview.h"
-#include "ui/text/format_values.h"
#include "ui/grouped_layout.h"
-#include "ui/text/text_options.h"
#include "ui/toast/toast.h"
#include "ui/controls/emoji_button.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/stickers/data_stickers.h"
#include "data/stickers/data_custom_emoji.h"
-#include "media/clip/media_clip_reader.h"
#include "api/api_common.h"
#include "window/window_session_controller.h"
#include "core/application.h"
#include "core/core_settings.h"
-#include "styles/style_chat.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
#include "styles/style_chat_helpers.h"
-#include "styles/style_info.h"
#include "styles/style_menu_icons.h"
#include
@@ -77,10 +70,14 @@ constexpr auto kMaxMessageLength = 4096;
using Ui::SendFilesWay;
-inline bool CanAddUrls(const QList &urls) {
+[[nodiscard]] inline bool CanAddUrls(const QList &urls) {
return !urls.isEmpty() && ranges::all_of(urls, &QUrl::isLocalFile);
}
+[[nodiscard]] bool CanAddFiles(not_null data) {
+ return data->hasImage() || CanAddUrls(Core::ReadMimeUrls(data));
+}
+
void FileDialogCallback(
FileDialog::OpenResult &&result,
Fn checkResult,
@@ -448,13 +445,15 @@ void SendFilesBox::setupDragArea() {
auto computeState = [=](const QMimeData *data) {
using DragState = Storage::MimeDataState;
const auto state = Storage::ComputeMimeDataState(data);
- return (state == DragState::PhotoFiles)
- ? DragState::Image
+ return (state == DragState::PhotoFiles || state == DragState::Image)
+ ? (_sendWay.current().sendImagesAsPhotos()
+ ? DragState::Image
+ : DragState::Files)
: state;
};
const auto areas = DragArea::SetupDragAreaToContainer(
this,
- [=](not_null d) { return canAddFiles(d); },
+ CanAddFiles,
[=](bool f) { _caption->setAcceptDrops(f); },
[=] { updateControlsGeometry(); },
std::move(computeState));
@@ -1049,7 +1048,7 @@ void SendFilesBox::setupCaption() {
not_null data,
Ui::InputField::MimeAction action) {
if (action == Ui::InputField::MimeAction::Check) {
- return canAddFiles(data);
+ return CanAddFiles(data);
} else if (action == Ui::InputField::MimeAction::Insert) {
return addFiles(data);
}
@@ -1145,10 +1144,6 @@ void SendFilesBox::captionResized() {
update();
}
-bool SendFilesBox::canAddFiles(not_null data) const {
- return data->hasImage() || CanAddUrls(Core::ReadMimeUrls(data));
-}
-
bool SendFilesBox::addFiles(not_null data) {
const auto premium = _show->session().premium();
auto list = [&] {
diff --git a/Telegram/SourceFiles/boxes/send_files_box.h b/Telegram/SourceFiles/boxes/send_files_box.h
index 6e6c6fa82..2325e911a 100644
--- a/Telegram/SourceFiles/boxes/send_files_box.h
+++ b/Telegram/SourceFiles/boxes/send_files_box.h
@@ -213,7 +213,6 @@ private:
void updateControlsGeometry();
void updateCaptionPlaceholder();
- bool canAddFiles(not_null data) const;
bool addFiles(not_null data);
bool addFiles(Ui::PreparedList list);
void addFile(Ui::PreparedFile &&file);
diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp
index 7cba7daba..154961016 100644
--- a/Telegram/SourceFiles/boxes/share_box.cpp
+++ b/Telegram/SourceFiles/boxes/share_box.cpp
@@ -18,7 +18,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/toast/toast.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/multi_select.h"
-#include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/popup_menu.h"
@@ -602,7 +601,7 @@ void ShareBox::submitWhenOnline() {
submit(Api::DefaultSendWhenOnlineOptions());
}
-void ShareBox::copyLink() {
+void ShareBox::copyLink() const {
if (const auto onstack = _descriptor.copyCallback) {
onstack();
}
diff --git a/Telegram/SourceFiles/boxes/share_box.h b/Telegram/SourceFiles/boxes/share_box.h
index b13638577..e793abb6e 100644
--- a/Telegram/SourceFiles/boxes/share_box.h
+++ b/Telegram/SourceFiles/boxes/share_box.h
@@ -119,7 +119,7 @@ private:
void submitSilent();
void submitScheduled();
void submitWhenOnline();
- void copyLink();
+ void copyLink() const;
bool searchByUsername(bool useCache = false);
SendMenu::Type sendMenuType() const;
diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp
index 237306d84..b1eef8d91 100644
--- a/Telegram/SourceFiles/boxes/stickers_box.cpp
+++ b/Telegram/SourceFiles/boxes/stickers_box.cpp
@@ -53,6 +53,22 @@ constexpr auto kArchivedLimitFirstRequest = 10;
constexpr auto kArchivedLimitPerPage = 30;
constexpr auto kHandleMegagroupSetAddressChangeTimeout = crl::time(1000);
+[[nodiscard]] QString FillSetTitle(
+ not_null 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
class StickersBox::CounterWidget : public Ui::RpWidget {
@@ -98,22 +114,25 @@ public:
void updateRows(); // refresh only pack cover stickers
bool appendSet(not_null set);
- StickersSetsOrder getOrder() const;
- StickersSetsOrder getFullOrder() const;
- StickersSetsOrder getRemovedSets() const;
+ StickersSetsOrder order() const;
+ StickersSetsOrder fullOrder() const;
+ StickersSetsOrder removedSets() const;
void setFullOrder(const StickersSetsOrder &order);
void setRemovedSets(const StickersSetsOrder &removed);
+ void setRowRemovedBySetId(uint64 setId, bool removed);
+
void setInstallSetCallback(Fn callback) {
_installSetCallback = std::move(callback);
}
+ void setRemoveSetCallback(Fn callback) {
+ _removeSetCallback = std::move(callback);
+ }
void setLoadMoreCallback(Fn callback) {
_loadMoreCallback = std::move(callback);
}
- void setMinHeight(int newWidth, int minHeight);
-
int getVisibleTop() const {
return _visibleTop;
}
@@ -151,13 +170,13 @@ private:
int32 pixh);
~Row();
- bool isRecentSet() const;
- bool isMasksSet() const;
- bool isEmojiSet() const;
- bool isWebm() const;
- bool isInstalled() const;
- bool isUnread() const;
- bool isArchived() const;
+ [[nodiscard]] bool isRecentSet() const;
+ [[nodiscard]] bool isMasksSet() const;
+ [[nodiscard]] bool isEmojiSet() const;
+ [[nodiscard]] bool isWebm() const;
+ [[nodiscard]] bool isInstalled() const;
+ [[nodiscard]] bool isUnread() const;
+ [[nodiscard]] bool isArchived() const;
const not_null set;
DocumentData *sticker = nullptr;
@@ -212,7 +231,11 @@ private:
void setPressed(SelectedRow pressed);
void setup();
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);
void paintRow(Painter &p, not_null row, int index);
@@ -237,10 +260,6 @@ private:
void rebuildAppendSet(not_null set);
void fillSetCover(not_null set, DocumentData **outSticker, int *outWidth, int *outHeight) const;
int fillSetCount(not_null set) const;
- [[nodiscard]] QString fillSetTitle(
- not_null set,
- int maxNameWidth,
- int *outTitleWidth) const;
[[nodiscard]] Data::StickersSetFlags fillSetFlags(
not_null set) const;
void rebuildMegagroupSet();
@@ -270,6 +289,7 @@ private:
Ui::Animations::Basic _shiftingAnimation;
Fn _installSetCallback;
+ Fn _removeSetCallback;
Fn _loadMoreCallback;
int _visibleTop = 0;
@@ -598,17 +618,43 @@ void StickersBox::prepare() {
_attached.widget()->hide();
}
- const auto installCallback = [=](uint64 setId) { installSet(setId); };
- if (_featured.widget()) {
- _featured.widget()->setInstallSetCallback(installCallback);
- }
- if (_archived.widget()) {
- _archived.widget()->setInstallSetCallback(installCallback);
- _archived.widget()->setLoadMoreCallback([=] { loadMoreArchived(); });
- }
- if (_attached.widget()) {
- _attached.widget()->setInstallSetCallback(installCallback);
- _attached.widget()->setLoadMoreCallback([=] { showAttachedStickers(); });
+ {
+ const auto installCallback = [=](uint64 setId) { installSet(setId); };
+ const auto markAsInstalledCallback = [=](uint64 setId) {
+ if (_installed.widget()) {
+ _installed.widget()->setRowRemovedBySetId(setId, false);
+ }
+ if (_featured.widget()) {
+ _featured.widget()->setRowRemovedBySetId(setId, false);
+ }
+ };
+ 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) {
@@ -634,7 +680,7 @@ void StickersBox::prepare() {
} else { // _section == Section::Featured
_tab = &_featured;
}
- setInnerWidget(_tab->takeWidget(), getTopSkip());
+ setInnerWidget(_tab->takeWidget(), topSkip());
setDimensions(st::boxWideWidth, st::boxMaxListHeight);
session().data().stickers().updated(_isEmoji
@@ -757,7 +803,7 @@ void StickersBox::paintEvent(QPaintEvent *e) {
Painter p(this);
if (_slideAnimation) {
- _slideAnimation->paintFrame(p, 0, getTopSkip(), width());
+ _slideAnimation->paintFrame(p, 0, topSkip(), width());
if (!_slideAnimation->animating()) {
_slideAnimation.reset();
setInnerVisible(true);
@@ -774,7 +820,7 @@ void StickersBox::updateTabsGeometry() {
_tabs->resizeToWidth(_tabIndices.size() * width() / maxTabs);
_unreadBadge->setVisible(_tabIndices.contains(Section::Featured));
- setInnerTopSkip(getTopSkip());
+ setInnerTopSkip(topSkip());
auto featuredLeft = width() / maxTabs;
auto featuredRight = 2 * width() / maxTabs;
@@ -790,7 +836,7 @@ void StickersBox::updateTabsGeometry() {
_tabs->moveToLeft(0, 0);
}
-int StickersBox::getTopSkip() const {
+int StickersBox::topSkip() const {
return _tabs ? (_tabs->height() - st::lineWidth) : 0;
}
@@ -819,8 +865,8 @@ void StickersBox::switchTab() {
}
if (_tab == &_installed) {
- _localOrder = _tab->widget()->getFullOrder();
- _localRemoved = _tab->widget()->getRemovedSets();
+ _localOrder = _tab->widget()->fullOrder();
+ _localRemoved = _tab->widget()->removedSets();
}
auto wasCache = grabContentCache();
auto wasIndex = _tab->index();
@@ -831,12 +877,12 @@ void StickersBox::switchTab() {
_tab->returnWidget(std::move(widget));
_tab = newTab;
_section = newSection;
- setInnerWidget(_tab->takeWidget(), getTopSkip());
+ setInnerWidget(_tab->takeWidget(), topSkip());
_tabs->raise();
_unreadBadge->raise();
_tab->widget()->show();
rebuildList();
- scrollToY(_tab->getScrollTop());
+ scrollToY(_tab->scrollTop());
setInnerVisible(true);
auto nowCache = grabContentCache();
auto nowIndex = _tab->index();
@@ -901,7 +947,7 @@ void StickersBox::installSet(uint64 setId) {
}
void StickersBox::installDone(
- const MTPmessages_StickerSetInstallResult &result) {
+ const MTPmessages_StickerSetInstallResult &result) const {
if (result.type() == mtpc_messages_stickerSetInstallResultArchive) {
session().data().stickers().applyArchivedResult(
result.c_messages_stickerSetInstallResultArchive());
@@ -998,12 +1044,12 @@ void StickersBox::rebuildList(Tab *tab) {
tab = _tab;
}
- if ((tab == &_installed) || (tab == &_masks)) {
- _localOrder = tab->widget()->getFullOrder();
- _localRemoved = tab->widget()->getRemovedSets();
+ if ((tab == &_installed) || (tab == &_masks) || (_tab == &_featured)) {
+ _localOrder = tab->widget()->fullOrder();
+ _localRemoved = tab->widget()->removedSets();
}
tab->widget()->rebuild(_isMasks);
- if ((tab == &_installed) || (tab == &_masks)) {
+ if ((tab == &_installed) || (tab == &_masks) || (_tab == &_featured)) {
tab->widget()->setFullOrder(_localOrder);
}
tab->widget()->setRemovedSets(_localRemoved);
@@ -1030,14 +1076,14 @@ void StickersBox::saveChanges() {
}
if (installed) {
session().api().saveStickerSets(
- installed->getOrder(),
- installed->getRemovedSets(),
+ installed->order(),
+ installed->removedSets(),
Data::StickersType::Stickers);
}
if (masks) {
session().api().saveStickerSets(
- masks->getOrder(),
- masks->getRemovedSets(),
+ masks->order(),
+ masks->removedSets(),
Data::StickersType::Masks);
}
}
@@ -1056,7 +1102,7 @@ const Data::StickersSetsOrder &StickersBox::archivedSetsOrder() const {
: session().data().stickers().archivedMaskSetsOrder();
}
-Data::StickersSetsOrder &StickersBox::archivedSetsOrderRef() {
+Data::StickersSetsOrder &StickersBox::archivedSetsOrderRef() const {
return !_isMasks
? session().data().stickers().archivedSetsOrderRef()
: session().data().stickers().archivedMaskSetsOrderRef();
@@ -1594,6 +1640,12 @@ void StickersBox::Inner::paintFakeButton(Painter &p, not_null row, int ind
const auto textWidth = _installedWidth;
const auto &text = _installedText;
_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.setPen(st.textFg);
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) {
auto rippleSize = QSize(_undoWidth - st::stickersUndoRemove.width, st::stickersUndoRemove.height);
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 {
auto rippleSize = st::stickersRemove.rippleAreaSize;
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) {
- auto rippleSize = QSize(_addWidth - st::stickersTrendingAdd.width, st::stickersTrendingAdd.height);
- auto rippleMask = Ui::RippleAnimation::RoundRectMask(rippleSize, st::roundRadiusLarge);
- ensureRipple(st::stickersTrendingAdd.ripple, std::move(rippleMask), removeButton);
+ } else {
+ const auto installedSet = row->isInstalled()
+ && !row->isArchived()
+ && !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) {
@@ -1747,9 +1810,14 @@ void StickersBox::Inner::setPressed(SelectedRow pressed) {
}
}
-void StickersBox::Inner::ensureRipple(const style::RippleAnimation &st, QImage mask, bool removeButton) {
- _rows[_actionDown]->ripple = std::make_unique(st, std::move(mask), [this, index = _actionDown, removeButton] {
- update(myrtlrect(relativeButtonRect(removeButton, false).translated(0, _itemsTop + index * _rowHeight)));
+void StickersBox::Inner::ensureRipple(
+ const style::RippleAnimation &st,
+ QImage mask,
+ bool removeButton,
+ bool installedSet) {
+ const auto dy = _itemsTop + _actionDown * _rowHeight;
+ _rows[_actionDown]->ripple = std::make_unique(st, std::move(mask), [=] {
+ update(myrtlrect(relativeButtonRect(removeButton, installedSet).translated(0, dy)));
});
}
@@ -1812,7 +1880,7 @@ void StickersBox::Inner::updateSelected() {
selected = selectedIndex;
local.setY(local.y() - _itemsTop - selectedIndex * _rowHeight);
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 rect = myrtlrect(relativeButtonRect(removeButton, false));
actionSel = rect.contains(local) ? selectedIndex : -1;
@@ -1857,7 +1925,7 @@ float64 StickersBox::Inner::aboveShadowOpacity() const {
auto dx = 0;
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) {
@@ -1868,10 +1936,11 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
_mouse = e->globalPos();
updateSelected();
if (_actionDown == _actionSel && _actionSel >= 0) {
- if (_isInstalledTab) {
- setRowRemoved(_actionDown, !_rows[_actionDown]->removed);
- } else if (_installSetCallback) {
- _installSetCallback(_rows[_actionDown]->set->id);
+ const auto callback = _rows[_actionDown]->removed
+ ? _installSetCallback
+ : _removeSetCallback;
+ if (callback) {
+ callback(_rows[_actionDown]->set->id);
}
} else if (_dragging >= 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) {
auto &row = _rows[index];
if (row->removed != removed) {
@@ -2114,7 +2190,7 @@ void StickersBox::Inner::rebuildMegagroupSet() {
auto removed = false;
auto maxNameWidth = countMaxNameWidth(!_isInstalledTab);
auto titleWidth = 0;
- auto title = fillSetTitle(set, maxNameWidth, &titleWidth);
+ auto title = FillSetTitle(set, maxNameWidth, &titleWidth);
if (!_megagroupSelectedSet
|| _megagroupSelectedSet->set->id != set->id) {
_megagroupSetField->setText(set->shortName);
@@ -2217,11 +2293,6 @@ void StickersBox::Inner::setMegagroupSelectedSet(
updateSelected();
}
-void StickersBox::Inner::setMinHeight(int newWidth, int minHeight) {
- _minHeight = minHeight;
- updateSize(newWidth);
-}
-
void StickersBox::Inner::updateSize(int newWidth) {
auto naturalHeight = _itemsTop + int(_rows.size()) * _rowHeight + st::membersMarginBottom;
resize(newWidth ? newWidth : width(), qMax(_minHeight, naturalHeight));
@@ -2269,7 +2340,7 @@ void StickersBox::Inner::updateRows() {
&& row->isInstalled()
&& !row->isArchived()
&& !row->removed);
- row->title = fillSetTitle(
+ row->title = FillSetTitle(
set,
installedSet ? maxNameWidthInstalled : maxNameWidth,
&row->titleWidth);
@@ -2331,7 +2402,7 @@ void StickersBox::Inner::rebuildAppendSet(not_null set) {
&& !(flagsOverride & SetFlag::Archived)
&& !removed);
int titleWidth = 0;
- QString title = fillSetTitle(set, maxNameWidth, &titleWidth);
+ QString title = FillSetTitle(set, maxNameWidth, &titleWidth);
int count = fillSetCount(set);
const auto existing = [&]{
@@ -2458,22 +2529,6 @@ int StickersBox::Inner::fillSetCount(not_null set) const {
return result + added;
}
-QString StickersBox::Inner::fillSetTitle(
- not_null 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(
not_null set) const {
const auto result = set->flags;
@@ -2494,19 +2549,19 @@ StickersSetsOrder StickersBox::Inner::collectSets(Check check) const {
return result;
}
-StickersSetsOrder StickersBox::Inner::getOrder() const {
+StickersSetsOrder StickersBox::Inner::order() const {
return collectSets([](Row *row) {
return !row->isArchived() && !row->removed && !row->isRecentSet();
});
}
-StickersSetsOrder StickersBox::Inner::getFullOrder() const {
+StickersSetsOrder StickersBox::Inner::fullOrder() const {
return collectSets([](Row *row) {
return !row->isRecentSet();
});
}
-StickersSetsOrder StickersBox::Inner::getRemovedSets() const {
+StickersSetsOrder StickersBox::Inner::removedSets() const {
return collectSets([](Row *row) {
return row->removed;
});
diff --git a/Telegram/SourceFiles/boxes/stickers_box.h b/Telegram/SourceFiles/boxes/stickers_box.h
index f0e54374a..e374ba0b4 100644
--- a/Telegram/SourceFiles/boxes/stickers_box.h
+++ b/Telegram/SourceFiles/boxes/stickers_box.h
@@ -104,7 +104,7 @@ private:
[[nodiscard]] int index() const;
void saveScrollTop();
- int getScrollTop() const {
+ int scrollTop() const {
return _scrollTop;
}
@@ -122,12 +122,12 @@ private:
void updateTabsGeometry();
void switchTab();
void installSet(uint64 setId);
- int getTopSkip() const;
+ int topSkip() const;
void saveChanges();
QPixmap grabContentCache();
- void installDone(const MTPmessages_StickerSetInstallResult &result);
+ void installDone(const MTPmessages_StickerSetInstallResult &result) const;
void installFail(const MTP::Error &error, uint64 setId);
void preloadArchivedSets();
@@ -139,7 +139,7 @@ private:
void showAttachedStickers();
const Data::StickersSetsOrder &archivedSetsOrder() const;
- Data::StickersSetsOrder &archivedSetsOrderRef();
+ Data::StickersSetsOrder &archivedSetsOrderRef() const;
std::array widgets() const;
diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style
index fb85201eb..20b1d6287 100644
--- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style
+++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style
@@ -294,8 +294,11 @@ stickersTrendingAdd: RoundButton(defaultActiveButton) {
stickersTrendingInstalled: RoundButton(stickersTrendingAdd) {
textFg: activeButtonBg;
textFgOver: activeButtonBgOver;
- textBg: activeButtonSecondaryFg;
- textBgOver: activeButtonSecondaryFgOver;
+ textBg: lightButtonBgOver;
+ textBgOver: lightButtonBgOver;
+ ripple: RippleAnimation(defaultRippleAnimation) {
+ color: activeButtonSecondaryFg;
+ }
}
stickersRemove: IconButton(defaultIconButton) {
width: 40px;
diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h
index c8ab4422d..325f6ccc4 100644
--- a/Telegram/SourceFiles/core/utils.h
+++ b/Telegram/SourceFiles/core/utils.h
@@ -45,12 +45,9 @@ inline QString IconName() {
inline bool CanReadDirectory(const QString &path) {
#ifndef Q_OS_MAC // directory_iterator since 10.15
- try {
- std::filesystem::directory_iterator(path.toStdString());
- return true;
- } catch (...) {
- return false;
- }
+ std::error_code error;
+ std::filesystem::directory_iterator(path.toStdString(), error);
+ return !error;
#else
Unexpected("Not implemented.");
#endif
diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h
index e6f40049b..fec69bc64 100644
--- a/Telegram/SourceFiles/core/version.h
+++ b/Telegram/SourceFiles/core/version.h
@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D666}"_cs;
constexpr auto AppNameOld = "AyuGram for Windows"_cs;
constexpr auto AppName = "AyuGram Desktop"_cs;
constexpr auto AppFile = "AyuGram"_cs;
-constexpr auto AppVersion = 4009003;
-constexpr auto AppVersionStr = "4.9.3";
+constexpr auto AppVersion = 4009004;
+constexpr auto AppVersionStr = "4.9.4";
constexpr auto AppBetaVersion = false;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
diff --git a/Telegram/SourceFiles/data/notify/data_notify_settings.cpp b/Telegram/SourceFiles/data/notify/data_notify_settings.cpp
index 1675d1e92..c522b8aa5 100644
--- a/Telegram/SourceFiles/data/notify/data_notify_settings.cpp
+++ b/Telegram/SourceFiles/data/notify/data_notify_settings.cpp
@@ -42,11 +42,39 @@ constexpr auto kMaxNotifyCheckDelay = 24 * 3600 * crl::time(1000);
return (result > 0);
}
+[[nodiscard]] bool SkipAddException(not_null 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
+DefaultNotify DefaultNotifyType(not_null 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 owner)
- : _owner(owner)
- , _unmuteByFinishedTimer([=] { unmuteByFinished(); }) {
+: _owner(owner)
+, _unmuteByFinishedTimer([=] { unmuteByFinished(); }) {
}
void NotifySettings::request(not_null peer) {
@@ -63,7 +91,7 @@ void NotifySettings::request(not_null peer) {
}
}
-void NotifySettings::request(not_null thread) {
+void NotifySettings::request(not_null thread) {
if (const auto topic = thread->asTopic()) {
if (topic->notify().settingsUnknown()) {
topic->session().api().requestNotifySettings(
@@ -145,6 +173,7 @@ void NotifySettings::apply(
not_null peer,
const MTPPeerNotifySettings &settings) {
if (peer->notify().change(settings)) {
+ updateException(peer);
updateLocal(peer);
Core::App().notifications().checkDelayed();
}
@@ -162,7 +191,7 @@ void NotifySettings::apply(
}
void NotifySettings::apply(
- not_null topic,
+ not_null topic,
const MTPPeerNotifySettings &settings) {
if (topic->notify().change(settings)) {
updateLocal(topic);
@@ -171,8 +200,8 @@ void NotifySettings::apply(
}
void NotifySettings::update(
- not_null thread,
- Data::MuteValue muteForSeconds,
+ not_null thread,
+ MuteValue muteForSeconds,
std::optional silentPosts,
std::optional sound,
std::optional storiesMuted) {
@@ -181,34 +210,29 @@ void NotifySettings::update(
silentPosts,
sound,
storiesMuted)) {
+ if (const auto history = thread->asHistory()) {
+ updateException(history->peer);
+ }
updateLocal(thread);
thread->session().api().updateNotifySettingsDelayed(thread);
}
}
-void NotifySettings::resetToDefault(not_null thread) {
- const auto empty = MTP_peerNotifySettings(
- MTP_flags(0),
- MTPBool(),
- MTPBool(),
- MTPint(),
- MTPNotificationSound(),
- MTPNotificationSound(),
- MTPNotificationSound(),
- MTPBool(),
- MTPBool(),
- MTPNotificationSound(),
- MTPNotificationSound(),
- MTPNotificationSound());
- if (thread->notify().change(empty)) {
+void NotifySettings::resetToDefault(not_null thread) {
+ // Duplicated in clearExceptions(type) and resetToDefault(peer).
+ if (thread->notify().resetToDefault()) {
+ if (const auto history = thread->asHistory()) {
+ updateException(history->peer);
+ }
updateLocal(thread);
thread->session().api().updateNotifySettingsDelayed(thread);
+ Core::App().notifications().checkDelayed();
}
}
void NotifySettings::update(
not_null peer,
- Data::MuteValue muteForSeconds,
+ MuteValue muteForSeconds,
std::optional silentPosts,
std::optional sound,
std::optional storiesMuted) {
@@ -217,33 +241,24 @@ void NotifySettings::update(
silentPosts,
sound,
storiesMuted)) {
+ updateException(peer);
updateLocal(peer);
peer->session().api().updateNotifySettingsDelayed(peer);
}
}
void NotifySettings::resetToDefault(not_null peer) {
- const auto empty = MTP_peerNotifySettings(
- MTP_flags(0),
- MTPBool(),
- MTPBool(),
- MTPint(),
- MTPNotificationSound(),
- MTPNotificationSound(),
- MTPNotificationSound(),
- MTPBool(),
- MTPBool(),
- MTPNotificationSound(),
- MTPNotificationSound(),
- MTPNotificationSound());
- if (peer->notify().change(empty)) {
+ // Duplicated in clearExceptions(type) and resetToDefault(thread).
+ if (peer->notify().resetToDefault()) {
+ updateException(peer);
updateLocal(peer);
peer->session().api().updateNotifySettingsDelayed(peer);
+ Core::App().notifications().checkDelayed();
}
}
-void NotifySettings::forumParentMuteUpdated(not_null forum) {
- forum->enumerateTopics([&](not_null topic) {
+void NotifySettings::forumParentMuteUpdated(not_null forum) {
+ forum->enumerateTopics([&](not_null topic) {
if (!topic->notify().settingsUnknown()) {
updateLocal(topic);
}
@@ -266,11 +281,7 @@ auto NotifySettings::defaultValue(DefaultNotify type) const
const PeerNotifySettings &NotifySettings::defaultSettings(
not_null peer) const {
- return defaultSettings(peer->isUser()
- ? DefaultNotify::User
- : (peer->isChat() || peer->isMegagroup())
- ? DefaultNotify::Group
- : DefaultNotify::Broadcast);
+ return defaultSettings(DefaultNotifyType(peer));
}
const PeerNotifySettings &NotifySettings::defaultSettings(
@@ -278,9 +289,16 @@ const PeerNotifySettings &NotifySettings::defaultSettings(
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(
DefaultNotify type,
- Data::MuteValue muteForSeconds,
+ MuteValue muteForSeconds,
std::optional silentPosts,
std::optional sound,
std::optional storiesMuted) {
@@ -291,7 +309,7 @@ void NotifySettings::defaultUpdate(
}
}
-void NotifySettings::updateLocal(not_null thread) {
+void NotifySettings::updateLocal(not_null thread) {
const auto topic = thread->asTopic();
if (!topic) {
return updateLocal(thread->peer());
@@ -351,7 +369,7 @@ void NotifySettings::cacheSound(not_null document) {
const auto view = document->createMediaView();
_ringtones.views.emplace(document->id, view);
document->forceToCache(true);
- document->save(Data::FileOriginRingtones(), QString());
+ document->save(FileOriginRingtones(), QString());
}
void NotifySettings::cacheSound(const std::optional &sound) {
@@ -459,7 +477,7 @@ void NotifySettings::unmuteByFinished() {
}
bool NotifySettings::isMuted(
- not_null thread,
+ not_null thread,
crl::time *changesIn) const {
const auto topic = thread->asTopic();
const auto until = topic ? topic->notify().muteUntil() : std::nullopt;
@@ -468,27 +486,24 @@ bool NotifySettings::isMuted(
: isMuted(thread->peer(), changesIn);
}
-bool NotifySettings::isMuted(not_null thread) const {
+bool NotifySettings::isMuted(not_null thread) const {
return isMuted(thread, nullptr);
}
-NotifySound NotifySettings::sound(
- not_null thread) const {
+NotifySound NotifySettings::sound(not_null thread) const {
const auto topic = thread->asTopic();
const auto sound = topic ? topic->notify().sound() : std::nullopt;
return sound ? *sound : this->sound(thread->peer());
}
-bool NotifySettings::muteUnknown(
- not_null thread) const {
+bool NotifySettings::muteUnknown(not_null thread) const {
const auto topic = thread->asTopic();
return (topic && topic->notify().settingsUnknown())
|| ((!topic || !topic->notify().muteUntil().has_value())
&& muteUnknown(thread->peer()));
}
-bool NotifySettings::soundUnknown(
- not_null thread) const {
+bool NotifySettings::soundUnknown(not_null thread) const {
const auto topic = thread->asTopic();
return (topic && topic->notify().settingsUnknown())
|| ((!topic || !topic->notify().sound().has_value())
@@ -543,8 +558,7 @@ bool NotifySettings::silentPostsUnknown(
&& defaultSettings(peer).settingsUnknown());
}
-bool NotifySettings::soundUnknown(
- not_null peer) const {
+bool NotifySettings::soundUnknown(not_null peer) const {
return peer->notify().settingsUnknown()
|| (!peer->notify().sound().has_value()
&& defaultSettings(peer).settingsUnknown());
@@ -556,8 +570,7 @@ bool NotifySettings::settingsUnknown(not_null peer) const {
|| soundUnknown(peer);
}
-bool NotifySettings::settingsUnknown(
- not_null thread) const {
+bool NotifySettings::settingsUnknown(not_null thread) const {
const auto topic = thread->asTopic();
return muteUnknown(thread)
|| soundUnknown(thread)
@@ -577,4 +590,85 @@ rpl::producer<> NotifySettings::defaultUpdates(
: DefaultNotify::Broadcast);
}
+void NotifySettings::loadExceptions() {
+ for (auto i = 0; i != kDefaultNotifyTypes; ++i) {
+ if (_exceptionsRequestId[i]) {
+ continue;
+ }
+ const auto type = static_cast(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 peer) {
+ const auto type = DefaultNotifyType(peer);
+ const auto index = static_cast(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(i));
+ }
+ }
+ });
+ }
+ _exceptionsUpdatesScheduled[static_cast(type)] = true;
+ _exceptionsUpdatesRealtime.fire_copy(type);
+}
+
+rpl::producer NotifySettings::exceptionsUpdates() const {
+ return _exceptionsUpdates.events();
+}
+
+auto NotifySettings::exceptionsUpdatesRealtime() const
+-> rpl::producer {
+ return _exceptionsUpdatesRealtime.events();
+}
+
+const base::flat_set> &NotifySettings::exceptions(
+ DefaultNotify type) const {
+ const auto index = static_cast(type);
+ Assert(index >= 0 && index < kDefaultNotifyTypes);
+
+ return _exceptions[index];
+}
+
+void NotifySettings::clearExceptions(DefaultNotify type) {
+ const auto index = static_cast(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
diff --git a/Telegram/SourceFiles/data/notify/data_notify_settings.h b/Telegram/SourceFiles/data/notify/data_notify_settings.h
index 6e51b87ac..b1930f45b 100644
--- a/Telegram/SourceFiles/data/notify/data_notify_settings.h
+++ b/Telegram/SourceFiles/data/notify/data_notify_settings.h
@@ -26,13 +26,17 @@ enum class DefaultNotify {
Group,
Broadcast,
};
+[[nodiscard]] DefaultNotify DefaultNotifyType(
+ not_null peer);
+
+[[nodiscard]] MTPInputNotifyPeer DefaultNotifyToMTP(DefaultNotify type);
class NotifySettings final {
public:
NotifySettings(not_null owner);
void request(not_null peer);
- void request(not_null thread);
+ void request(not_null thread);
void apply(
const MTPNotifyPeer ¬ifyPeer,
@@ -50,25 +54,25 @@ public:
MsgId topicRootId,
const MTPPeerNotifySettings &settings);
void apply(
- not_null topic,
+ not_null topic,
const MTPPeerNotifySettings &settings);
void update(
- not_null thread,
- Data::MuteValue muteForSeconds,
+ not_null thread,
+ MuteValue muteForSeconds,
std::optional silentPosts = std::nullopt,
std::optional sound = std::nullopt,
std::optional storiesMuted = std::nullopt);
- void resetToDefault(not_null thread);
+ void resetToDefault(not_null thread);
void update(
not_null peer,
- Data::MuteValue muteForSeconds,
+ MuteValue muteForSeconds,
std::optional silentPosts = std::nullopt,
std::optional sound = std::nullopt,
std::optional storiesMuted = std::nullopt);
void resetToDefault(not_null peer);
- void forumParentMuteUpdated(not_null forum);
+ void forumParentMuteUpdated(not_null forum);
void cacheSound(DocumentId id);
void cacheSound(not_null document);
@@ -81,21 +85,19 @@ public:
[[nodiscard]] const PeerNotifySettings &defaultSettings(
DefaultNotify type) const;
+ [[nodiscard]] bool isMuted(DefaultNotify type) const;
void defaultUpdate(
DefaultNotify type,
- Data::MuteValue muteForSeconds,
+ MuteValue muteForSeconds,
std::optional silentPosts = std::nullopt,
std::optional sound = std::nullopt,
std::optional storiesMuted = std::nullopt);
- [[nodiscard]] bool isMuted(not_null thread) const;
- [[nodiscard]] NotifySound sound(
- not_null thread) const;
- [[nodiscard]] bool muteUnknown(
- not_null thread) const;
- [[nodiscard]] bool soundUnknown(
- not_null thread) const;
+ [[nodiscard]] bool isMuted(not_null thread) const;
+ [[nodiscard]] NotifySound sound(not_null thread) const;
+ [[nodiscard]] bool muteUnknown(not_null thread) const;
+ [[nodiscard]] bool soundUnknown(not_null thread) const;
[[nodiscard]] bool isMuted(not_null peer) const;
[[nodiscard]] bool silentPosts(not_null peer) const;
@@ -105,7 +107,17 @@ public:
not_null peer) const;
[[nodiscard]] bool soundUnknown(not_null peer) const;
+ void loadExceptions();
+ [[nodiscard]] rpl::producer exceptionsUpdates() const;
+ [[nodiscard]] auto exceptionsUpdatesRealtime() const
+ -> rpl::producer;
+ [[nodiscard]] const base::flat_set> &exceptions(
+ DefaultNotify type) const;
+ void clearExceptions(DefaultNotify type);
+
private:
+ static constexpr auto kDefaultNotifyTypes = 3;
+
struct DefaultValue {
PeerNotifySettings settings;
rpl::event_stream<> updates;
@@ -114,7 +126,7 @@ private:
void cacheSound(const std::optional &sound);
[[nodiscard]] bool isMuted(
- not_null thread,
+ not_null thread,
crl::time *changesIn) const;
[[nodiscard]] bool isMuted(
not_null peer,
@@ -126,21 +138,22 @@ private:
not_null peer) const;
[[nodiscard]] bool settingsUnknown(not_null peer) const;
[[nodiscard]] bool settingsUnknown(
- not_null thread) const;
+ not_null thread) const;
void unmuteByFinished();
void unmuteByFinishedDelayed(crl::time delay);
- void updateLocal(not_null thread);
+ void updateLocal(not_null thread);
void updateLocal(not_null peer);
void updateLocal(DefaultNotify type);
+ void updateException(not_null peer);
+ void exceptionsUpdated(DefaultNotify type);
+
const not_null _owner;
DefaultValue _defaultValues[3];
std::unordered_set> _mutedPeers;
- std::unordered_map<
- not_null,
- rpl::lifetime> _mutedTopics;
+ std::unordered_map, rpl::lifetime> _mutedTopics;
base::Timer _unmuteByFinishedTimer;
struct {
@@ -151,6 +164,14 @@ private:
rpl::lifetime pendingLifetime;
} _ringtones;
+ rpl::event_stream _exceptionsUpdates;
+ rpl::event_stream _exceptionsUpdatesRealtime;
+ std::array<
+ base::flat_set>,
+ kDefaultNotifyTypes> _exceptions;
+ std::array _exceptionsRequestId = {};
+ std::array _exceptionsUpdatesScheduled = {};
+
};
} // namespace Data
diff --git a/Telegram/SourceFiles/data/notify/data_peer_notify_settings.cpp b/Telegram/SourceFiles/data/notify/data_peer_notify_settings.cpp
index 4b756a611..48d198d6e 100644
--- a/Telegram/SourceFiles/data/notify/data_peer_notify_settings.cpp
+++ b/Telegram/SourceFiles/data/notify/data_peer_notify_settings.cpp
@@ -256,6 +256,15 @@ bool PeerNotifySettings::change(
SerializeSound(std::nullopt))); // stories_sound
}
+bool PeerNotifySettings::resetToDefault() {
+ if (_known && !_value) {
+ return false;
+ }
+ _known = true;
+ _value = nullptr;
+ return true;
+}
+
std::optional PeerNotifySettings::muteUntil() const {
return _value
? _value->muteUntil()
diff --git a/Telegram/SourceFiles/data/notify/data_peer_notify_settings.h b/Telegram/SourceFiles/data/notify/data_peer_notify_settings.h
index 76a8ecfd9..b5de9e119 100644
--- a/Telegram/SourceFiles/data/notify/data_peer_notify_settings.h
+++ b/Telegram/SourceFiles/data/notify/data_peer_notify_settings.h
@@ -46,6 +46,7 @@ public:
std::optional silentPosts,
std::optional sound,
std::optional storiesMuted);
+ bool resetToDefault();
bool settingsUnknown() const;
std::optional muteUntil() const;
diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style
index 622016bb8..854faef97 100644
--- a/Telegram/SourceFiles/dialogs/dialogs.style
+++ b/Telegram/SourceFiles/dialogs/dialogs.style
@@ -23,6 +23,12 @@ DialogRow {
unreadMarkDiameter: pixels;
}
+ThreeStateIcon {
+ icon: icon;
+ over: icon;
+ active: icon;
+}
+
ForumTopicIcon {
size: pixels;
font: font;
@@ -316,38 +322,56 @@ dialogSearchFrom: IconButton(dialogCalendar) {
}
dialogsChatTypeSkip: 3px;
-dialogsChatIcon: icon {{ "dialogs/dialogs_chat", dialogsChatIconFg, point(1px, 4px) }};
-dialogsChatIconOver: icon {{ "dialogs/dialogs_chat", dialogsChatIconFgOver, point(1px, 4px) }};
-dialogsChatIconActive: icon {{ "dialogs/dialogs_chat", dialogsChatIconFgActive, point(1px, 4px) }};
-dialogsChannelIcon: icon {{ "dialogs/dialogs_channel", dialogsChatIconFg, point(3px, 4px) }};
-dialogsChannelIconOver: icon {{ "dialogs/dialogs_channel", dialogsChatIconFgOver, point(3px, 4px) }};
-dialogsChannelIconActive: icon {{ "dialogs/dialogs_channel", dialogsChatIconFgActive, point(3px, 4px) }};
-dialogsBotIcon: icon {{ "dialogs/dialogs_bot", dialogsChatIconFg, point(1px, 3px) }};
-dialogsBotIconOver: icon {{ "dialogs/dialogs_bot", dialogsChatIconFgOver, point(1px, 3px) }};
-dialogsBotIconActive: icon {{ "dialogs/dialogs_bot", dialogsChatIconFgActive, point(1px, 3px) }};
-dialogsForumIcon: icon {{ "dialogs/dialogs_forum", dialogsChatIconFg, point(1px, 4px) }};
-dialogsForumIconOver: icon {{ "dialogs/dialogs_forum", dialogsChatIconFgOver, point(1px, 4px) }};
-dialogsForumIconActive: icon {{ "dialogs/dialogs_forum", dialogsChatIconFgActive, point(1px, 4px) }};
+dialogsChatIcon: ThreeStateIcon {
+ icon: icon {{ "dialogs/dialogs_chat", dialogsChatIconFg, point(1px, 4px) }};
+ over: icon {{ "dialogs/dialogs_chat", dialogsChatIconFgOver, point(1px, 4px) }};
+ active: icon {{ "dialogs/dialogs_chat", dialogsChatIconFgActive, point(1px, 4px) }};
+}
+dialogsChannelIcon: ThreeStateIcon {
+ icon: icon {{ "dialogs/dialogs_channel", dialogsChatIconFg, point(3px, 4px) }};
+ over: icon {{ "dialogs/dialogs_channel", dialogsChatIconFgOver, point(3px, 4px) }};
+ active: icon {{ "dialogs/dialogs_channel", dialogsChatIconFgActive, point(3px, 4px) }};
+}
+dialogsBotIcon: ThreeStateIcon {
+ 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 }};
dialogsRepliesUserpic: icon {{ "replies_userpic", historyPeerUserpicFg }};
dialogsInaccessibleUserpic: icon {{ "dialogs/inaccessible_userpic", historyPeerUserpicFg }};
dialogsSendStateSkip: 20px;
-dialogsSendingIcon: icon {{ "dialogs/dialogs_sending", dialogsSendingIconFg, point(8px, 4px) }};
-dialogsSendingIconOver: icon {{ "dialogs/dialogs_sending", dialogsSendingIconFgOver, point(8px, 4px) }};
-dialogsSendingIconActive: icon {{ "dialogs/dialogs_sending", dialogsSendingIconFgActive, point(8px, 4px) }};
-dialogsSentIcon: icon {{ "dialogs/dialogs_sent", dialogsSentIconFg, point(10px, 4px) }};
-dialogsSentIconOver: icon {{ "dialogs/dialogs_sent", dialogsSentIconFgOver, point(10px, 4px) }};
-dialogsSentIconActive: icon {{ "dialogs/dialogs_sent", dialogsSentIconFgActive, point(10px, 4px) }};
-dialogsReceivedIcon: icon {{ "dialogs/dialogs_received", dialogsSentIconFg, point(5px, 4px) }};
-dialogsReceivedIconOver: icon {{ "dialogs/dialogs_received", dialogsSentIconFgOver, point(5px, 4px) }};
-dialogsReceivedIconActive: icon {{ "dialogs/dialogs_received", dialogsSentIconFgActive, point(5px, 4px) }};
-dialogsPinnedIcon: icon {{ "dialogs/dialogs_pinned", dialogsUnreadBgMuted }};
-dialogsPinnedIconOver: icon {{ "dialogs/dialogs_pinned", dialogsUnreadBgMutedOver }};
-dialogsPinnedIconActive: icon {{ "dialogs/dialogs_pinned", dialogsUnreadBgMutedActive }};
-dialogsLockIcon: icon {{ "emoji/premium_lock", dialogsUnreadBgMuted, point(4px, 0px) }};
-dialogsLockIconOver: icon {{ "emoji/premium_lock", dialogsUnreadBgMutedOver, point(4px, 0px) }};
-dialogsLockIconActive: icon {{ "emoji/premium_lock", dialogsUnreadBgMutedActive, point(4px, 0px) }};
+dialogsSendingIcon: ThreeStateIcon {
+ icon: icon {{ "dialogs/dialogs_sending", dialogsSendingIconFg, point(8px, 4px) }};
+ over: icon {{ "dialogs/dialogs_sending", dialogsSendingIconFgOver, point(8px, 4px) }};
+ active: icon {{ "dialogs/dialogs_sending", dialogsSendingIconFgActive, point(8px, 4px) }};
+}
+dialogsSentIcon: ThreeStateIcon {
+ icon: icon {{ "dialogs/dialogs_sent", dialogsSentIconFg, point(10px, 4px) }};
+ over: icon {{ "dialogs/dialogs_sent", dialogsSentIconFgOver, point(10px, 4px) }};
+ active: icon {{ "dialogs/dialogs_sent", dialogsSentIconFgActive, point(10px, 4px) }};
+}
+dialogsReceivedIcon: ThreeStateIcon {
+ icon: icon {{ "dialogs/dialogs_received", dialogsSentIconFg, point(5px, 4px) }};
+ over: icon {{ "dialogs/dialogs_received", dialogsSentIconFgOver, point(5px, 4px) }};
+ active: icon {{ "dialogs/dialogs_received", dialogsSentIconFgActive, point(5px, 4px) }};
+}
+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 {
{ "dialogs/dialogs_verified_star", dialogsVerifiedIconBg },
@@ -361,9 +385,11 @@ dialogsVerifiedIconActive: icon {
{ "dialogs/dialogs_verified_star", dialogsVerifiedIconBgActive },
{ "dialogs/dialogs_verified_check", dialogsVerifiedIconFgActive },
};
-dialogsPremiumIcon: icon {{ "dialogs/dialogs_premium", dialogsVerifiedIconBg }};
-dialogsPremiumIconOver: icon {{ "dialogs/dialogs_premium", dialogsVerifiedIconBgOver }};
-dialogsPremiumIconActive: icon {{ "dialogs/dialogs_premium", dialogsVerifiedIconBgActive }};
+dialogsPremiumIcon: ThreeStateIcon {
+ icon: icon {{ "dialogs/dialogs_premium", dialogsVerifiedIconBg }};
+ over: icon {{ "dialogs/dialogs_premium", dialogsVerifiedIconBgOver }};
+ active: icon {{ "dialogs/dialogs_premium", dialogsVerifiedIconBgActive }};
+}
historySendingIcon: icon {{ "dialogs/dialogs_sending", historySendingOutIconFg, point(5px, 5px) }};
historySendingInvertedIcon: icon {{ "dialogs/dialogs_sending", historySendingInvertedIconFg, point(5px, 5px) }};
@@ -436,12 +462,28 @@ dialogsMiniPreviewSkip: 2px;
dialogsMiniPreviewRight: 3px;
dialogsMiniPlay: icon{{ "dialogs/dialogs_mini_play", videoPlayIconFg }};
-dialogsUnreadMention: icon{{ "dialogs/dialogs_mention", dialogsUnreadFg }};
-dialogsUnreadMentionOver: icon{{ "dialogs/dialogs_mention", dialogsUnreadFgOver }};
-dialogsUnreadMentionActive: icon{{ "dialogs/dialogs_mention", dialogsUnreadFgActive }};
-dialogsUnreadReaction: icon{{ "dialogs/dialogs_reaction", dialogsUnreadFg }};
-dialogsUnreadReactionOver: icon{{ "dialogs/dialogs_reaction", dialogsUnreadFgOver }};
-dialogsUnreadReactionActive: icon{{ "dialogs/dialogs_reaction", dialogsUnreadFgActive }};
+dialogsMiniForwardIcon: ThreeStateIcon {
+ icon: icon {{ "mini_forward", dialogsTextFg, point(0px, 1px) }};
+ over: icon {{ "mini_forward", dialogsTextFgOver, point(0px, 1px) }};
+ active: icon {{ "mini_forward", dialogsTextFgActive, point(0px, 1px) }};
+}
+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;
downloadArrow: icon{{ "fast_to_original", menuIconFg }};
diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp
index 697862166..eadcc89ee 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp
+++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "dialogs/dialogs_inner_widget.h"
+#include "dialogs/dialogs_three_state_icon.h"
#include "dialogs/ui/dialogs_layout.h"
#include "dialogs/ui/dialogs_stories_content.h"
#include "dialogs/ui/dialogs_stories_list.h"
@@ -1010,11 +1011,10 @@ void InnerWidget::paintPeerSearchResult(
: context.selected
? &st::dialogsVerifiedIconOver
: &st::dialogsVerifiedIcon),
- .premium = (context.active
- ? &st::dialogsPremiumIconActive
- : context.selected
- ? &st::dialogsPremiumIconOver
- : &st::dialogsPremiumIcon),
+ .premium = &ThreeStateIcon(
+ st::dialogsPremiumIcon,
+ context.active,
+ context.selected),
.scam = (context.active
? &st::dialogsScamFgActive
: context.selected
diff --git a/Telegram/SourceFiles/dialogs/dialogs_main_list.cpp b/Telegram/SourceFiles/dialogs/dialogs_main_list.cpp
index b9c7b2c7b..9e643555a 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_main_list.cpp
+++ b/Telegram/SourceFiles/dialogs/dialogs_main_list.cpp
@@ -194,6 +194,11 @@ UnreadState MainList::unreadState() const {
result.chatsMuted = result.chats;
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;
}
diff --git a/Telegram/SourceFiles/dialogs/dialogs_three_state_icon.h b/Telegram/SourceFiles/dialogs/dialogs_three_state_icon.h
new file mode 100644
index 000000000..e150fc669
--- /dev/null
+++ b/Telegram/SourceFiles/dialogs/dialogs_three_state_icon.h
@@ -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
diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp
index 91674bfed..3ef60a4fe 100644
--- a/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp
+++ b/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_forum_topic.h"
#include "data/data_session.h"
#include "dialogs/dialogs_list.h"
+#include "dialogs/dialogs_three_state_icon.h"
#include "dialogs/ui/dialogs_video_userpic.h"
#include "styles/style_dialogs.h"
#include "styles/style_window.h"
@@ -147,11 +148,10 @@ int PaintBadges(
const auto badge = PaintUnreadBadge(p, counter, right, top, st);
right -= badge.width() + st.padding;
} else if (displayPinnedIcon) {
- const auto &icon = context.active
- ? st::dialogsPinnedIconActive
- : context.selected
- ? st::dialogsPinnedIconOver
- : st::dialogsPinnedIcon;
+ const auto &icon = ThreeStateIcon(
+ st::dialogsPinnedIcon,
+ context.active,
+ context.selected);
icon.paint(p, right - icon.width(), pinnedIconTop, context.width);
right -= icon.width() + st::dialogsUnreadPadding;
}
@@ -169,17 +169,12 @@ int PaintBadges(
st.textTop = 0;
const auto counter = QString();
const auto badge = PaintUnreadBadge(p, counter, right, top, st);
- (badgesState.mention
- ? (st.active
- ? st::dialogsUnreadMentionActive
- : st.selected
- ? st::dialogsUnreadMentionOver
- : st::dialogsUnreadMention)
- : (st.active
- ? st::dialogsUnreadReactionActive
- : st.selected
- ? st::dialogsUnreadReactionOver
- : st::dialogsUnreadReaction)).paintInCenter(p, badge);
+ ThreeStateIcon(
+ badgesState.mention
+ ? st::dialogsUnreadMention
+ : st::dialogsUnreadReaction,
+ st.active,
+ st.selected).paintInCenter(p, badge);
right -= badge.width() + st.padding + st::dialogsUnreadPadding;
}
return (initial - right);
@@ -437,11 +432,10 @@ void PaintRow(
auto availableWidth = namewidth;
if (entry->isPinnedDialog(context.filter)
&& (context.filter || !entry->fixedOnTopIndex())) {
- auto &icon = context.active
- ? st::dialogsPinnedIconActive
- : context.selected
- ? st::dialogsPinnedIconOver
- : st::dialogsPinnedIcon;
+ auto &icon = ThreeStateIcon(
+ st::dialogsPinnedIcon,
+ context.active,
+ context.selected);
icon.paint(
p,
context.width - context.st->padding.right() - icon.width(),
@@ -527,11 +521,10 @@ void PaintRow(
auto availableWidth = namewidth;
if (entry->isPinnedDialog(context.filter)
&& (context.filter || !entry->fixedOnTopIndex())) {
- auto &icon = context.active
- ? st::dialogsPinnedIconActive
- : context.selected
- ? st::dialogsPinnedIconOver
- : st::dialogsPinnedIcon;
+ auto &icon = ThreeStateIcon(
+ st::dialogsPinnedIcon,
+ context.active,
+ context.selected);
icon.paint(p, context.width - context.st->padding.right() - icon.width(), texttop, context.width);
availableWidth -= icon.width() + st::dialogsUnreadPadding;
}
@@ -561,51 +554,49 @@ void PaintRow(
paintItemCallback(nameleft, namewidth);
} else if (entry->isPinnedDialog(context.filter)
&& (context.filter || !entry->fixedOnTopIndex())) {
- auto &icon = context.active
- ? st::dialogsPinnedIconActive
- : context.selected
- ? st::dialogsPinnedIconOver
- : st::dialogsPinnedIcon;
- icon.paint(p, context.width - context.st->padding.right() - icon.width(), texttop, context.width);
+ auto &icon = ThreeStateIcon(
+ st::dialogsPinnedIcon,
+ context.active,
+ context.selected);
+ icon.paint(
+ p,
+ context.width - context.st->padding.right() - icon.width(),
+ texttop,
+ context.width);
}
const auto sendStateIcon = [&]() -> const style::icon* {
if (!thread) {
return nullptr;
} else if (const auto topic = thread->asTopic()
; !context.search && topic && topic->closed()) {
- return &(context.active
- ? st::dialogsLockIconActive
- : context.selected
- ? st::dialogsLockIconOver
- : st::dialogsLockIcon);
+ return &ThreeStateIcon(
+ st::dialogsLockIcon,
+ context.active,
+ context.selected);
} else if (draft) {
if (draft->saveRequestId) {
- return &(context.active
- ? st::dialogsSendingIconActive
- : context.selected
- ? st::dialogsSendingIconOver
- : st::dialogsSendingIcon);
+ return &ThreeStateIcon(
+ st::dialogsSendingIcon,
+ context.active,
+ context.selected);
}
} else if (item && !item->isEmpty() && item->needCheck()) {
if (!item->isSending() && !item->hasFailed()) {
if (item->unread(thread)) {
- return &(context.active
- ? st::dialogsSentIconActive
- : context.selected
- ? st::dialogsSentIconOver
- : st::dialogsSentIcon);
+ return &ThreeStateIcon(
+ st::dialogsSentIcon,
+ context.active,
+ context.selected);
}
- return &(context.active
- ? st::dialogsReceivedIconActive
- : context.selected
- ? st::dialogsReceivedIconOver
- : st::dialogsReceivedIcon);
+ return &ThreeStateIcon(
+ st::dialogsReceivedIcon,
+ context.active,
+ context.selected);
}
- return &(context.active
- ? st::dialogsSendingIconActive
- : context.selected
- ? st::dialogsSendingIconOver
- : st::dialogsSendingIcon);
+ return &ThreeStateIcon(
+ st::dialogsSendingIcon,
+ context.active,
+ context.selected);
}
return nullptr;
}();
@@ -643,11 +634,10 @@ void PaintRow(
: context.selected
? &st::dialogsVerifiedIconOver
: &st::dialogsVerifiedIcon),
- .premium = (context.active
- ? &st::dialogsPremiumIconActive
- : context.selected
- ? &st::dialogsPremiumIconOver
- : &st::dialogsPremiumIcon),
+ .premium = &ThreeStateIcon(
+ st::dialogsPremiumIcon,
+ context.active,
+ context.selected),
.scam = (context.active
? &st::dialogsScamFgActive
: context.selected
@@ -710,30 +700,26 @@ const style::icon *ChatTypeIcon(
const PaintContext &context) {
if (const auto user = peer->asUser()) {
if (ShowUserBotIcon(user)) {
- return &(context.active
- ? st::dialogsBotIconActive
- : context.selected
- ? st::dialogsBotIconOver
- : st::dialogsBotIcon);
+ return &ThreeStateIcon(
+ st::dialogsBotIcon,
+ context.active,
+ context.selected);
}
} else if (peer->isBroadcast()) {
- return &(context.active
- ? st::dialogsChannelIconActive
- : context.selected
- ? st::dialogsChannelIconOver
- : st::dialogsChannelIcon);
+ return &ThreeStateIcon(
+ st::dialogsChannelIcon,
+ context.active,
+ context.selected);
} else if (peer->isForum()) {
- return &(context.active
- ? st::dialogsForumIconActive
- : context.selected
- ? st::dialogsForumIconOver
- : st::dialogsForumIcon);
+ return &ThreeStateIcon(
+ st::dialogsForumIcon,
+ context.active,
+ context.selected);
} else {
- return &(context.active
- ? st::dialogsChatIconActive
- : context.selected
- ? st::dialogsChatIconOver
- : st::dialogsChatIcon);
+ return &ThreeStateIcon(
+ st::dialogsChatIcon,
+ context.active,
+ context.selected);
}
return nullptr;
}
diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp
index b1f59fb43..253bdc967 100644
--- a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp
+++ b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp
@@ -11,12 +11,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item.h"
#include "history/view/history_view_item_preview.h"
#include "main/main_session.h"
+#include "dialogs/dialogs_three_state_icon.h"
#include "dialogs/ui/dialogs_layout.h"
#include "dialogs/ui/dialogs_topics_view.h"
#include "ui/effects/spoiler_mess.h"
#include "ui/text/text_options.h"
#include "ui/text/text_utilities.h"
-#include "ui/image/image.h"
#include "ui/painter.h"
#include "ui/power_saving.h"
#include "core/ui_integration.h"
@@ -159,6 +159,11 @@ void MessageView::prepare(
options.ignoreTopic = true;
options.spoilerLoginCode = true;
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 history = item->history();
const auto context = Core::MarkedTextContext{
@@ -169,7 +174,7 @@ void MessageView::prepare(
const auto senderTill = (preview.arrowInTextPosition > 0)
? preview.arrowInTextPosition
: preview.imagesInTextPosition;
- if (hasImages && senderTill > 0) {
+ if ((hasImages || _leftIcon) && senderTill > 0) {
auto sender = Text::Mid(preview.text, 0, senderTill);
TextUtilities::Trim(sender);
_senderCache.setMarkedText(
@@ -314,6 +319,15 @@ void MessageView::paint(
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) {
if (rect.width() < st::dialogsMiniPreview) {
break;
diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h
index 87b69b435..14f677536 100644
--- a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h
+++ b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h
@@ -15,6 +15,7 @@ enum class ImageRoundRadius;
namespace style {
struct DialogRow;
+struct ThreeStateIcon;
} // namespace style
namespace Ui {
@@ -92,6 +93,7 @@ private:
mutable std::vector _imagesCache;
mutable std::unique_ptr _spoiler;
mutable std::unique_ptr _loadingContext;
+ mutable const style::ThreeStateIcon *_leftIcon = nullptr;
};
diff --git a/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp b/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp
index e105cd441..a2072b065 100644
--- a/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp
+++ b/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp
@@ -10,19 +10,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/algorithm.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
+#endif // !DESKTOP_APP_USE_PACKAGED && !Q_OS_WIN && !Q_OS_MAC
+
#include
#ifdef LIB_FFMPEG_USE_QT_PRIVATE_API
#include
#endif // LIB_FFMPEG_USE_QT_PRIVATE_API
-#include
-
extern "C" {
#include
-#if !defined DESKTOP_APP_USE_PACKAGED && !defined Q_OS_WIN && !defined Q_OS_MAC
-#include
-#endif // !DESKTOP_APP_USE_PACKAGED && !Q_OS_WIN && !Q_OS_MAC
} // extern "C"
namespace FFmpeg {
@@ -95,19 +95,10 @@ void PremultiplyLine(uchar *dst, const uchar *src, int intsCount) {
auto list = std::deque{
AV_PIX_FMT_CUDA,
};
- const auto vdpau = [&] {
- if (const auto handle = dlopen("libvdpau.so.1", RTLD_LAZY)) {
- dlclose(handle);
- }
- if (dlerror()) {
- return false;
- }
- return true;
- }();
- if (vdpau) {
+ if (base::Platform::LoadLibrary("libvdpau.so.1")) {
list.push_front(AV_PIX_FMT_VDPAU);
}
- const auto va = [&] {
+ if ([&] {
const auto list = std::array{
"libva-drm.so.1",
"libva-x11.so.1",
@@ -115,16 +106,12 @@ void PremultiplyLine(uchar *dst, const uchar *src, int intsCount) {
"libdrm.so.2",
};
for (const auto lib : list) {
- if (const auto handle = dlopen(lib, RTLD_LAZY)) {
- dlclose(handle);
- }
- if (dlerror()) {
+ if (!base::Platform::LoadLibrary(lib)) {
return false;
}
}
return true;
- }();
- if (va) {
+ }()) {
list.push_front(AV_PIX_FMT_VAAPI);
}
return list;
diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp
index 595c253ce..918f10bfd 100644
--- a/Telegram/SourceFiles/history/history_inner_widget.cpp
+++ b/Telegram/SourceFiles/history/history_inner_widget.cpp
@@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_inner_widget.h"
#include "core/file_utilities.h"
-#include "core/crash_reports.h"
#include "core/click_handler_types.h"
#include "history/history.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_multiline_action.h"
#include "ui/widgets/popup_menu.h"
-#include "ui/image/image.h"
#include "ui/effects/path_shift_gradient.h"
#include "ui/effects/message_sending_animation_controller.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/layers/generic_box.h"
#include "ui/controls/delete_message_context_action.h"
-#include "ui/controls/who_reacted_context_action.h"
#include "ui/painter.h"
#include "ui/ui_utility.h"
-#include "ui/cached_round_corners.h"
#include "ui/inactive_press.h"
-#include "window/window_adaptive.h"
#include "window/window_session_controller.h"
#include "window/window_controller.h"
#include "window/window_peer_menu.h"
-#include "window/window_controller.h"
#include "window/notifications_manager.h"
#include "boxes/about_sponsored_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_histories.h"
#include "data/data_changes.h"
-#include "data/stickers/data_stickers.h"
#include "data/data_sponsored_messages.h"
#include "dialogs/ui/dialogs_video_userpic.h"
#include "settings/settings_premium.h"
#include "styles/style_chat.h"
-#include "styles/style_window.h" // st::windowMinWidth
#include "styles/style_menu_icons.h"
#include
diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp
index 8ed9b9cac..13e52a6f7 100644
--- a/Telegram/SourceFiles/history/history_item.cpp
+++ b/Telegram/SourceFiles/history/history_item.cpp
@@ -15,17 +15,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_message.h"
#include "history/view/history_view_service_message.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_helpers.h"
#include "history/history_unread_things.h"
#include "history/history.h"
#include "mtproto/mtproto_config.h"
#include "media/clip/media_clip_reader.h"
-#include "ui/effects/ripple_animation.h"
#include "ui/text/format_values.h"
#include "ui/text/text_isolated_emoji.h"
-#include "ui/text/text_options.h"
#include "ui/text/text_utilities.h"
#include "storage/file_upload.h"
#include "storage/storage_facade.h"
@@ -41,7 +38,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwindow.h"
#include "window/window_controller.h"
#include "window/window_session_controller.h"
-#include "core/crash_reports.h"
#include "core/click_handler_types.h"
#include "base/unixtime.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 "payments/payments_checkout_process.h" // CheckoutProcess::Start.
#include "styles/style_dialogs.h"
-#include "styles/style_chat.h"
#include "ayu/ayu_settings.h"
@@ -2972,6 +2967,11 @@ ItemPreview HistoryItem::toPreview(ToPreviewOptions options) const {
? tr::lng_from_you(tr::now)
: sender->shortName();
};
+ result.icon = (Get() != nullptr)
+ ? ItemPreview::Icon::ForwardedMessage
+ : replyToStory().valid()
+ ? ItemPreview::Icon::ReplyToStory
+ : ItemPreview::Icon::None;
const auto fromForwarded = [&]() -> std::optional {
if (const auto forwarded = Get()) {
return forwarded->originalSender
diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp
index 9e82f9d06..59ab3396c 100644
--- a/Telegram/SourceFiles/history/history_item_components.cpp
+++ b/Telegram/SourceFiles/history/history_item_components.cpp
@@ -43,6 +43,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_bot.h"
#include "styles/style_widgets.h"
#include "styles/style_chat.h"
+#include "styles/style_dialogs.h" // dialogsMiniReplyStoryIcon.
#include
@@ -460,7 +461,14 @@ void HistoryMessageReply::updateName(
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 {
maxReplyWidth = st::msgDateFont->width(statePhrase());
}
@@ -596,14 +604,27 @@ void HistoryMessageReply::paint(
? stm->historyTextFg
: st->msgImgReplyBarColor());
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, {
- .position = QPoint(
- x + st::msgReplyBarSkip + previewSkip,
- y + st::msgReplyPadding.top() + st::msgServiceNameFont->height),
+ .position = replyToTextPosition,
.availableWidth = w - st::msgReplyBarSkip - previewSkip,
- .palette = &(inBubble
- ? stm->replyTextPalette
- : st->imgReplyTextPalette()),
+ .palette = replyToTextPalette,
.spoiler = Ui::Text::DefaultSpoilerCache(),
.now = context.now,
.pausedEmoji = (context.paused
diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index cf9466ba6..ea4b41222 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_chat_participants.h"
#include "api/api_report.h"
#include "api/api_sending.h"
-#include "api/api_text_entities.h"
#include "api/api_send_progress.h"
#include "api/api_unread_things.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/dropdown_menu.h"
#include "ui/widgets/labels.h"
-#include "ui/widgets/shadow.h"
#include "ui/effects/ripple_animation.h"
#include "ui/effects/message_sending_animation_controller.h"
#include "ui/text/text_utilities.h" // Ui::Text::ToUpper
#include "ui/text/format_values.h"
-#include "ui/chat/forward_options_box.h"
#include "ui/chat/message_bar.h"
#include "ui/chat/attach/attach_send_files_way.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_photo.h"
#include "data/data_photo_media.h"
-#include "data/data_media_types.h"
#include "data/data_channel.h"
#include "data/data_chat.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/media/history_view_media.h"
#include "profile/profile_block_group_members.h"
-#include "info/info_memento.h"
#include "core/click_handler_types.h"
#include "chat_helpers/tabbed_panel.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/widgets/popup_menu.h"
#include "ui/item_text_options.h"
-#include "ui/unread_badge.h"
#include "main/main_session.h"
#include "main/main_session_settings.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 "info/profile/info_profile_values.h" // SharedMediaCountValue.
#include "chat_helpers/emoji_suggestions_widget.h"
-#include "core/crash_reports.h"
#include "core/shortcuts.h"
#include "core/ui_integration.h"
#include "support/support_common.h"
@@ -160,12 +153,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "support/support_preload.h"
#include "dialogs/dialogs_key.h"
#include "calls/calls_instance.h"
-#include "api/api_bot.h"
#include "styles/style_chat.h"
#include "styles/style_dialogs.h"
#include "styles/style_window.h"
#include "styles/style_boxes.h"
-#include "styles/style_profile.h"
#include "styles/style_chat_helpers.h"
#include "styles/style_info.h"
diff --git a/Telegram/SourceFiles/history/view/history_view_item_preview.h b/Telegram/SourceFiles/history/view/history_view_item_preview.h
index 7ac0a2b18..f2304c49e 100644
--- a/Telegram/SourceFiles/history/view/history_view_item_preview.h
+++ b/Telegram/SourceFiles/history/view/history_view_item_preview.h
@@ -23,11 +23,17 @@ struct ItemPreviewImage {
};
struct ItemPreview {
+ enum class Icon {
+ None,
+ ForwardedMessage,
+ ReplyToStory,
+ };
TextWithEntities text;
std::vector images;
int arrowInTextPosition = -1;
int imagesInTextPosition = 0;
std::any loadingContext;
+ Icon icon = Icon::None;
};
struct ToPreviewOptions {
diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp
index e6f7fbcad..aa453c225 100644
--- a/Telegram/SourceFiles/history/view/history_view_message.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_message.cpp
@@ -759,7 +759,9 @@ QSize Message::performCountOptimalSize() {
: item->hiddenSenderInfo()->nameText();
auto namew = st::msgPadding.left()
+ name.maxWidth()
- + (_fromNameStatus ? st::dialogsPremiumIcon.width() : 0)
+ + (_fromNameStatus
+ ? st::dialogsPremiumIcon.icon.width()
+ : 0)
+ st::msgPadding.right();
if (via && !displayForwardedFrom()) {
namew += st::msgServiceFont->spacew + via->maxWidth
@@ -1358,7 +1360,7 @@ void Message::paintFromName(
return &info->nameText();
}();
const auto statusWidth = _fromNameStatus
- ? st::dialogsPremiumIcon.width()
+ ? st::dialogsPremiumIcon.icon.width()
: 0;
if (statusWidth && availableWidth > statusWidth) {
const auto x = availableLeft
@@ -1398,7 +1400,7 @@ void Message::paintFromName(
.paused = context.paused || On(PowerSaving::kEmojiStatus),
});
} else {
- st::dialogsPremiumIcon.paint(p, x, y, width(), color);
+ st::dialogsPremiumIcon.icon.paint(p, x, y, width(), color);
}
availableWidth -= statusWidth;
}
@@ -1407,7 +1409,8 @@ void Message::paintFromName(
nameText->drawElided(p, availableLeft, trect.top(), availableWidth);
const auto skipWidth = nameText->maxWidth()
+ (_fromNameStatus
- ? (st::dialogsPremiumIcon.width() + st::msgServiceFont->spacew)
+ ? (st::dialogsPremiumIcon.icon.width()
+ + st::msgServiceFont->spacew)
: 0)
+ st::msgServiceFont->spacew;
availableLeft += skipWidth;
@@ -3525,7 +3528,7 @@ void Message::fromNameUpdated(int width) const {
- st::msgPadding.right()
- nameText->maxWidth()
+ (_fromNameStatus
- ? (st::dialogsPremiumIcon.width()
+ ? (st::dialogsPremiumIcon.icon.width()
+ st::msgServiceFont->spacew)
: 0)
- st::msgServiceFont->spacew);
diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp
index 6f9275ae0..80b546b53 100644
--- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp
@@ -566,7 +566,7 @@ void TopBarWidget::paintTopBar(Painter &p) {
{
.peer = peer,
.verified = &st::dialogsVerifiedIcon,
- .premium = &st::dialogsPremiumIcon,
+ .premium = &st::dialogsPremiumIcon.icon,
.scam = &st::attentionButtonFg,
.premiumFg = &st::dialogsVerifiedIconBg,
.customEmojiRepaint = [=] { update(); },
diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp
index e24577962..c07fc9269 100644
--- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp
+++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp
@@ -241,7 +241,7 @@ int Selector::countWidth(int desiredWidth, int maxWidth) {
return std::max(2 * _skipx + _columns * _size, desiredWidth);
}
-QMargins Selector::extentsForShadow() const {
+QMargins Selector::marginsForShadow() const {
const auto line = st::lineWidth;
return useTransparency()
? st::reactionCornerShadow
@@ -264,26 +264,26 @@ void Selector::setSpecialExpandTopSkip(int skip) {
}
void Selector::initGeometry(int innerTop) {
- const auto extents = extentsForShadow();
+ const auto margins = marginsForShadow();
const auto parent = parentWidget()->rect();
const auto innerWidth = 2 * _skipx + _columns * _size;
const auto innerHeight = st::reactStripHeight;
const auto width = _useTransparency
- ? (innerWidth + extents.left() + extents.right())
+ ? (innerWidth + margins.left() + margins.right())
: 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);
_collapsedTopSkip = _useTransparency
? (extendTopForCategories() + _specialExpandTopSkip)
: 0;
- const auto top = innerTop - extents.top() - _collapsedTopSkip;
- const auto add = _st.icons.stripBubble.height() - extents.bottom();
+ const auto top = innerTop - margins.top() - _collapsedTopSkip;
+ const auto add = _st.icons.stripBubble.height() - margins.bottom();
_outer = QRect(0, _collapsedTopSkip, width, height);
_outerWithBubble = _outer.marginsAdded({ 0, 0, 0, add });
setGeometry(_outerWithBubble.marginsAdded(
{ 0, _collapsedTopSkip, 0, 0 }
).translated(left, top));
- _inner = _outer.marginsRemoved(extents);
+ _inner = _outer.marginsRemoved(margins);
if (!_strip) {
expand();
@@ -343,9 +343,9 @@ void Selector::paintAppearing(QPainter &p) {
}
_paintBuffer.fill(_st.bg->c);
auto q = QPainter(&_paintBuffer);
- const auto extents = extentsForShadow();
+ const auto margins = marginsForShadow();
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());
q.translate(_inner.topLeft() - QPoint(0, _collapsedTopSkip));
@@ -455,7 +455,7 @@ auto Selector::paintExpandingBg(QPainter &p, float64 progress)
const auto radius = _reactions.customAllowed
? (radiusStart + progress * (radiusEnd - radiusStart))
: radiusStart;
- const auto extents = extentsForShadow();
+ const auto margins = marginsForShadow();
const auto expanding = anim::easeOutCirc(1., progress);
const auto expandUp = anim::interpolate(0, _collapsedTopSkip, expanding);
const auto expandDown = anim::interpolate(
@@ -470,7 +470,7 @@ auto Selector::paintExpandingBg(QPainter &p, float64 progress)
p.fillRect(fill, _st.bg);
}
} else {
- const auto inner = outer.marginsRemoved(extentsForShadow());
+ const auto inner = outer.marginsRemoved(marginsForShadow());
p.fillRect(inner, _st.bg);
p.fillRect(
inner.x(),
@@ -483,7 +483,7 @@ auto Selector::paintExpandingBg(QPainter &p, float64 progress)
0,
extendTopForCategories(),
expanding);
- const auto inner = outer.marginsRemoved(extents);
+ const auto inner = outer.marginsRemoved(margins);
_shadowTop = inner.y() + categories;
_shadowSkip = (_useTransparency && categories < radius)
? int(base::SafeRound(
@@ -494,7 +494,7 @@ auto Selector::paintExpandingBg(QPainter &p, float64 progress)
.list = inner.marginsRemoved({ 0, categories, 0, 0 }),
.radius = radius,
.expanding = expanding,
- .finalBottom = height() - extents.bottom(),
+ .finalBottom = height() - margins.bottom(),
};
}
@@ -521,7 +521,7 @@ void Selector::paintExpanded(QPainter &p) {
if (_useTransparency) {
p.drawImage(0, 0, _paintBuffer);
} else {
- const auto inner = rect().marginsRemoved(extentsForShadow());
+ const auto inner = rect().marginsRemoved(marginsForShadow());
p.fillRect(inner, _st.bg);
p.fillRect(
inner.x(),
@@ -694,13 +694,13 @@ void Selector::expand() {
_willExpand.fire({});
preloadAllRecentsAnimations();
const auto parent = parentWidget()->geometry();
- const auto extents = extentsForShadow();
+ const auto margins = marginsForShadow();
const auto heightLimit = _reactions.customAllowed
? st::emojiPanMaxHeight
: minimalHeight();
const auto willBeHeight = std::min(
parent.height() - y(),
- extents.top() + heightLimit + extents.bottom());
+ margins.top() + heightLimit + margins.bottom());
const auto additionalBottom = willBeHeight - height();
const auto additional = _specialExpandTopSkip + additionalBottom;
if (additionalBottom < 0 || additional <= 0) {
@@ -834,7 +834,7 @@ void Selector::createList() {
_list->jumpedToPremium(
) | rpl::start_with_next(_jumpedToPremium, _list->lifetime());
- const auto inner = rect().marginsRemoved(extentsForShadow());
+ const auto inner = rect().marginsRemoved(marginsForShadow());
const auto footer = _reactions.customAllowed
? _list->createFooter().data()
: nullptr;
@@ -904,16 +904,16 @@ bool AdjustMenuGeometryForSelector(
const auto desiredWidth = menu->menu()->width() + added;
const auto maxWidth = menu->st().menu.widthMax + added;
const auto width = selector->countWidth(desiredWidth, maxWidth);
- const auto extents = selector->extentsForShadow();
+ const auto margins = selector->marginsForShadow();
const auto categoriesTop = selector->useTransparency()
? selector->extendTopForCategories()
: 0;
menu->setForceWidth(width - added);
const auto height = menu->height();
- const auto fullTop = extents.top() + categoriesTop + extend.top();
- const auto minimalHeight = extents.top()
+ const auto fullTop = margins.top() + categoriesTop + extend.top();
+ const auto minimalHeight = margins.top()
+ selector->minimalHeight()
- + extents.bottom();
+ + margins.bottom();
const auto willBeHeightWithoutBottomPadding = fullTop
+ height
- menu->st().shadow.extend.top();
@@ -924,15 +924,15 @@ bool AdjustMenuGeometryForSelector(
? (minimalHeight - willBeHeightWithoutBottomPadding)
: 0);
menu->setAdditionalMenuPadding(QMargins(
- extents.left() + extend.left(),
+ margins.left() + extend.left(),
fullTop,
- extents.right() + extend.right(),
+ margins.right() + extend.right(),
additionalPaddingBottom
), QMargins(
- extents.left(),
- extents.top(),
- extents.right(),
- std::min(additionalPaddingBottom, extents.bottom())
+ margins.left(),
+ margins.top(),
+ margins.right(),
+ std::min(additionalPaddingBottom, margins.bottom())
));
if (!menu->prepareGeometryFor(desiredPosition)) {
return false;
@@ -944,14 +944,14 @@ bool AdjustMenuGeometryForSelector(
return true;
}
menu->setAdditionalMenuPadding(QMargins(
- extents.left() + extend.left(),
+ margins.left() + extend.left(),
fullTop + additionalPaddingBottom,
- extents.right() + extend.right(),
+ margins.right() + extend.right(),
0
), QMargins(
- extents.left(),
- extents.top(),
- extents.right(),
+ margins.left(),
+ margins.top(),
+ margins.right(),
0
));
selector->setSpecialExpandTopSkip(additionalPaddingBottom);
diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h
index 8d3b34a03..bf9ddb72b 100644
--- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h
+++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h
@@ -61,7 +61,7 @@ public:
[[nodiscard]] bool useTransparency() const;
int countWidth(int desiredWidth, int maxWidth);
- [[nodiscard]] QMargins extentsForShadow() const;
+ [[nodiscard]] QMargins marginsForShadow() const;
[[nodiscard]] int extendTopForCategories() const;
[[nodiscard]] int minimalHeight() const;
[[nodiscard]] int countAppearedWidth(float64 progress) const;
diff --git a/Telegram/SourceFiles/media/stories/media_stories_reactions.cpp b/Telegram/SourceFiles/media/stories/media_stories_reactions.cpp
index 02b42d593..8057829e3 100644
--- a/Telegram/SourceFiles/media/stories/media_stories_reactions.cpp
+++ b/Telegram/SourceFiles/media/stories/media_stories_reactions.cpp
@@ -243,18 +243,18 @@ void Reactions::Panel::create() {
const auto desiredWidth = st::storiesReactionsWidth;
const auto maxWidth = desiredWidth * 2;
const auto width = _selector->countWidth(desiredWidth, maxWidth);
- const auto extents = _selector->extentsForShadow();
+ const auto margins = _selector->marginsForShadow();
const auto categoriesTop = _selector->extendTopForCategories();
- const auto full = extents.left() + width + extents.right();
+ const auto full = margins.left() + width + margins.right();
_shownValue = 0.;
rpl::combine(
_controller->layoutValue(),
_shownValue.value()
) | rpl::start_with_next([=](const Layout &layout, float64 shown) {
- const auto width = extents.left()
+ const auto width = margins.left()
+ _selector->countAppearedWidth(shown)
- + extents.right();
+ + margins.right();
const auto height = layout.reactions.height();
const auto shift = (width / 2);
const auto right = (mode == Mode::Message)
@@ -271,7 +271,7 @@ void Reactions::Panel::create() {
const auto innerTop = height
- st::storiesReactionsBottomSkip
- st::reactStripHeight;
- const auto maxAdded = innerTop - extents.top() - categoriesTop;
+ const auto maxAdded = innerTop - margins.top() - categoriesTop;
const auto added = std::min(maxAdded, st::storiesReactionsAddedTop);
_selector->setSpecialExpandTopSkip(added);
_selector->initGeometry(innerTop);
diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp
index fbf5e385a..8062ccc9f 100644
--- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp
+++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp
@@ -88,7 +88,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session_settings.h"
#include "layout/layout_document_generic_preview.h"
#include "platform/platform_overlay_widget.h"
-#include "settings/settings_premium.h"
#include "storage/file_download.h"
#include "storage/storage_account.h"
#include "calls/calls_instance.h"
@@ -104,7 +103,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include
#include
#include
-#include
#include
#include
diff --git a/Telegram/SourceFiles/menu/menu_mute.cpp b/Telegram/SourceFiles/menu/menu_mute.cpp
index 444747584..dd58f6fc0 100644
--- a/Telegram/SourceFiles/menu/menu_mute.cpp
+++ b/Telegram/SourceFiles/menu/menu_mute.cpp
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "data/data_thread.h"
#include "data/notify/data_notify_settings.h"
+#include "data/notify/data_peer_notify_settings.h"
#include "info/profile/info_profile_values.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
@@ -31,10 +32,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_menu_icons.h"
namespace MuteMenu {
-
namespace {
constexpr auto kMuteDurSecondsDefault = crl::time(8) * 3600;
+constexpr auto kMuteForeverValue = std::numeric_limits::max();
class IconWithText final : public Ui::Menu::Action {
public:
@@ -70,7 +71,7 @@ public:
MuteItem(
not_null parent,
const style::Menu &st,
- not_null thread);
+ Descriptor descriptor);
protected:
void paintEvent(QPaintEvent *e) override;
@@ -79,31 +80,30 @@ private:
const QPoint _itemIconPosition;
Ui::Animations::Simple _animation;
bool _isMuted = false;
+ bool _inited;
};
MuteItem::MuteItem(
not_null parent,
const style::Menu &st,
- not_null thread)
+ Descriptor descriptor)
: Ui::Menu::Action(
parent,
st,
Ui::CreateChild(parent.get()),
nullptr,
nullptr)
-, _itemIconPosition(st.itemIconPosition)
-, _isMuted(thread->owner().notifySettings().isMuted(thread)) {
- Info::Profile::NotificationsEnabledValue(
- thread
- ) | rpl::start_with_next([=](bool isUnmuted) {
- const auto isMuted = !isUnmuted;
+, _itemIconPosition(st.itemIconPosition) {
+ descriptor.isMutedValue(
+ ) | rpl::start_with_next([=](bool isMuted) {
action()->setText(isMuted
? tr::lng_mute_menu_duration_unmute(tr::now)
: tr::lng_mute_menu_duration_forever(tr::now));
- if (isMuted == _isMuted) {
+ if (_inited && isMuted == _isMuted) {
return;
}
+ _inited = true;
_isMuted = isMuted;
_animation.start(
[=] { update(); },
@@ -112,13 +112,8 @@ MuteItem::MuteItem(
st::defaultPopupMenu.showDuration);
}, lifetime());
- const auto weak = base::make_weak(thread);
setClickedCallback([=] {
- if (const auto strong = weak.get()) {
- strong->owner().notifySettings().update(
- strong,
- { .unmute = _isMuted, .forever = !_isMuted });
- }
+ descriptor.updateMutePeriod(_isMuted ? 0 : kMuteForeverValue);
});
}
@@ -140,7 +135,7 @@ void MuteItem::paintEvent(QPaintEvent *e) {
icon.paint(p, _itemIconPosition, width(), color);
}
-void MuteBox(not_null box, not_null thread) {
+void MuteBox(not_null box, Descriptor descriptor) {
struct State {
int lastSeconds = 0;
};
@@ -161,14 +156,9 @@ void MuteBox(not_null box, not_null thread) {
: tr::lng_mute_menu_mute();
}) | rpl::flatten_latest();
- const auto weak = base::make_weak(thread);
Ui::ConfirmBox(box, {
.confirmed = [=] {
- if (const auto strong = weak.get()) {
- strong->owner().notifySettings().update(
- strong,
- { .period = state->lastSeconds });
- }
+ descriptor.updateMutePeriod(state->lastSeconds);
box->getDelegate()->hideLayer();
},
.confirmText = std::move(confirmText),
@@ -178,7 +168,7 @@ void MuteBox(not_null box, not_null thread) {
void PickMuteBox(
not_null box,
- not_null thread) {
+ Descriptor descriptor) {
struct State {
base::unique_qptr menu;
};
@@ -191,17 +181,12 @@ void PickMuteBox(
const auto pickerCallback = TimePickerBox(box, seconds, phrases, 0);
- const auto weak = base::make_weak(thread);
Ui::ConfirmBox(box, {
.confirmed = [=] {
const auto muteFor = pickerCallback();
- if (const auto strong = weak.get()) {
- strong->owner().notifySettings().update(
- strong,
- { .period = muteFor });
- strong->session().settings().addMutePeriod(muteFor);
- strong->session().saveSettings();
- }
+ descriptor.updateMutePeriod(muteFor);
+ descriptor.session->settings().addMutePeriod(muteFor);
+ descriptor.session->saveSettings();
box->closeBox();
},
.confirmText = tr::lng_mute_menu_mute(),
@@ -220,11 +205,7 @@ void PickMuteBox(
st::popupMenuWithIcons);
state->menu->addAction(
tr::lng_manage_messages_ttl_after_custom(tr::now),
- [=] {
- if (const auto strong = weak.get()) {
- box->getDelegate()->show(Box(MuteBox, strong));
- }
- },
+ [=] { box->getDelegate()->show(Box(MuteBox, descriptor)); },
&st::menuIconCustomize);
state->menu->setDestroyedCallback(crl::guard(top, [=] {
top->setForceRippled(false);
@@ -236,46 +217,123 @@ void PickMuteBox(
} // namespace
+Descriptor ThreadDescriptor(not_null thread) {
+ const auto weak = base::make_weak(thread);
+ const auto isMutedValue = [=]() -> rpl::producer {
+ 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();
+ };
+ 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 session,
+ Data::DefaultNotify type) {
+ const auto settings = &session->data().notifySettings();
+ const auto isMutedValue = [=]() -> rpl::producer {
+ 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(
not_null menu,
- not_null thread,
+ Descriptor descriptor,
std::shared_ptr show) {
- const auto weak = base::make_weak(thread);
- const auto with = [=](Fn thread)> handler) {
- return [=] {
- if (const auto strong = weak.get()) {
- handler(strong);
- }
- };
+ const auto session = descriptor.session;
+ const auto soundSelect = [=] {
+ if (const auto currentSound = descriptor.currentSound()) {
+ show->showBox(Box(
+ RingtonesBox,
+ session,
+ *currentSound,
+ descriptor.updateSound));
+ }
};
-
menu->addAction(
tr::lng_mute_menu_sound_select(tr::now),
- with([=](not_null thread) {
- show->showBox(Box(ThreadRingtonesBox, thread));
- }),
+ soundSelect,
&st::menuIconSoundSelect);
- const auto notifySettings = &thread->owner().notifySettings();
- const auto soundIsNone = notifySettings->sound(thread).none;
+ const auto soundIsNone = descriptor.currentSound().value_or(
+ Data::NotifySound()
+ ).none;
+ const auto toggleSound = [=] {
+ if (auto sound = descriptor.currentSound()) {
+ sound->none = !soundIsNone;
+ descriptor.updateSound(*sound);
+ }
+ };
menu->addAction(
- soundIsNone
+ (soundIsNone
? tr::lng_mute_menu_sound_on(tr::now)
- : tr::lng_mute_menu_sound_off(tr::now),
- with([=](not_null thread) {
- auto sound = notifySettings->sound(thread);
- sound.none = !sound.none;
- notifySettings->update(thread, {}, {}, sound);
- }),
+ : tr::lng_mute_menu_sound_off(tr::now)),
+ toggleSound,
soundIsNone ? &st::menuIconSoundOn : &st::menuIconSoundOff);
const auto &st = menu->st().menu;
const auto iconTextPosition = st.itemIconPosition
+ st::menuIconMuteForAnyTextPosition;
- for (const auto muteFor : thread->session().settings().mutePeriods()) {
- const auto callback = with([=](not_null thread) {
- notifySettings->update(thread, { .period = muteFor });
- });
+ for (const auto muteFor : session->settings().mutePeriods()) {
+ const auto callback = [=, update = descriptor.updateMutePeriod] {
+ update(muteFor);
+ };
auto item = base::make_unique_q(
menu,
@@ -295,20 +353,17 @@ void FillMuteMenu(
menu->addAction(
tr::lng_mute_menu_duration(tr::now),
- with([=](not_null thread) {
- DEBUG_LOG(("Mute Info: PickMuteBox called."));
- show->showBox(Box(PickMuteBox, thread));
- }),
+ [=] { show->showBox(Box(PickMuteBox, descriptor)); },
&st::menuIconMuteFor);
menu->addAction(
- base::make_unique_q(menu, menu->st().menu, thread));
+ base::make_unique_q(menu, menu->st().menu, descriptor));
}
void SetupMuteMenu(
not_null parent,
rpl::producer<> triggers,
- Fn makeThread,
+ Fn()> makeDescriptor,
std::shared_ptr show) {
struct State {
base::unique_qptr menu;
@@ -319,11 +374,11 @@ void SetupMuteMenu(
) | rpl::start_with_next([=] {
if (state->menu) {
return;
- } else if (const auto thread = makeThread()) {
+ } else if (const auto descriptor = makeDescriptor()) {
state->menu = base::make_unique_q(
parent,
st::popupMenuWithIcons);
- FillMuteMenu(state->menu.get(), thread, show);
+ FillMuteMenu(state->menu.get(), *descriptor, show);
state->menu->popup(QCursor::pos());
}
}, parent->lifetime());
diff --git a/Telegram/SourceFiles/menu/menu_mute.h b/Telegram/SourceFiles/menu/menu_mute.h
index 045cd4d11..a6d818668 100644
--- a/Telegram/SourceFiles/menu/menu_mute.h
+++ b/Telegram/SourceFiles/menu/menu_mute.h
@@ -9,8 +9,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Data {
class Thread;
+struct NotifySound;
+enum class DefaultNotify;
} // namespace Data
+namespace Main {
+class Session;
+} // namespace Main
+
namespace Ui {
class PopupMenu;
class RpWidget;
@@ -19,15 +25,48 @@ class Show;
namespace MuteMenu {
+struct Descriptor {
+ not_null session;
+ Fn()> isMutedValue;
+ Fn()> currentSound;
+ Fn updateSound;
+ Fn updateMutePeriod;
+};
+
+[[nodiscard]] Descriptor ThreadDescriptor(not_null thread);
+[[nodiscard]] Descriptor DefaultDescriptor(
+ not_null session,
+ Data::DefaultNotify type);
+
void FillMuteMenu(
not_null menu,
- not_null thread,
+ Descriptor descriptor,
std::shared_ptr show);
void SetupMuteMenu(
not_null parent,
rpl::producer<> triggers,
- Fn makeThread,
+ Fn()> makeDescriptor,
std::shared_ptr show);
+inline void FillMuteMenu(
+ not_null menu,
+ not_null thread,
+ std::shared_ptr show) {
+ FillMuteMenu(menu, ThreadDescriptor(thread), std::move(show));
+}
+
+inline void SetupMuteMenu(
+ not_null parent,
+ rpl::producer<> triggers,
+ Fn makeThread,
+ std::shared_ptr show) {
+ SetupMuteMenu(parent, std::move(triggers), [=] {
+ const auto thread = makeThread();
+ return thread
+ ? ThreadDescriptor(thread)
+ : std::optional();
+ }, std::move(show));
+}
+
} // namespace MuteMenu
diff --git a/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp
index 9efcb0b68..a425ff59f 100644
--- a/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp
+++ b/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp
@@ -10,8 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/platform/base_platform_info.h"
#include "base/platform/linux/base_linux_xdp_utilities.h"
#include "base/platform/linux/base_linux_wayland_integration.h"
-#include "core/application.h"
-#include "window/window_controller.h"
#include "base/random.h"
#include
@@ -60,26 +58,13 @@ bool ShowXDPOpenWithDialog(const QString &filepath) {
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")
+ std::to_string(base::RandomValue());
const auto activationToken = []() -> Glib::ustring {
using base::Platform::WaylandIntegration;
if (const auto integration = WaylandIntegration::Instance()) {
- if (const auto token = integration->activationToken()
- ; !token.isNull()) {
- return token.toStdString();
- }
+ return integration->activationToken().toStdString();
}
return {};
}();
@@ -124,7 +109,7 @@ bool ShowXDPOpenWithDialog(const QString &filepath) {
kXDPOpenURIInterface,
"OpenFile",
Glib::create_variant(std::tuple{
- parentWindowId,
+ base::Platform::XDP::ParentWindowID(),
Glib::DBusHandle(),
std::map{
{
diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp
index 93bd9420b..ac74fd5c5 100644
--- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp
+++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp
@@ -139,11 +139,13 @@ void XCBSetDesktopFileName(QWindow *window) {
void SkipTaskbar(QWindow *window, bool skip) {
if (const auto integration = WaylandIntegration::Instance()) {
integration->skipTaskbar(window, skip);
+ return;
}
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
if (IsX11()) {
XCBSkipTaskbar(window, skip);
+ return;
}
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
}
diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp
index 84ceb77c2..b3b7a50e5 100644
--- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp
+++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp
@@ -17,10 +17,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwindow.h"
#include "storage/localstorage.h"
#include "core/launcher.h"
-#include "core/application.h"
#include "core/core_settings.h"
#include "core/update_checker.h"
-#include "window/window_controller.h"
#include "webview/platform/linux/webview_linux_webkitgtk.h"
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
@@ -65,16 +63,6 @@ bool PortalAutostart(bool start, bool silent) {
const auto connection = Gio::DBus::Connection::get_sync(
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")
+ std::to_string(base::RandomValue());
@@ -152,7 +140,7 @@ bool PortalAutostart(bool start, bool silent) {
"org.freedesktop.portal.Background",
"RequestBackground",
Glib::create_variant(std::tuple{
- parentWindowId,
+ base::Platform::XDP::ParentWindowID(),
options,
}),
base::Platform::XDP::kService);
diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style
index 4f53c9a33..45173b569 100644
--- a/Telegram/SourceFiles/settings/settings.style
+++ b/Telegram/SourceFiles/settings/settings.style
@@ -523,10 +523,19 @@ settingsPremiumLock: icon{{ "emoji/premium_lock", windowActiveTextFg, point(0px,
settingsPremiumLockSkip: 3px;
settingsBlockedListSubtitleAddPadding: margins(0px, 1px, 0px, -4px);
-settingsBlockedListIconPadding: margins(0px, 34px, 0px, 5px);
+settingsBlockedListIconPadding: margins(0px, 24px, 0px, 5px);
settingsBlockedList: PeerList(peerListBox) {
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) {
minWidth: 240px;
diff --git a/Telegram/SourceFiles/settings/settings_blocked_peers.cpp b/Telegram/SourceFiles/settings/settings_blocked_peers.cpp
index 45f2376df..e21666d81 100644
--- a/Telegram/SourceFiles/settings/settings_blocked_peers.cpp
+++ b/Telegram/SourceFiles/settings/settings_blocked_peers.cpp
@@ -47,7 +47,10 @@ Blocked::Blocked(
tr::lng_contacts_loading(),
st::changePhoneDescription),
std::move(padding)));
- Ui::ResizeFitChild(this, _loading.get());
+ Ui::ResizeFitChild(
+ this,
+ _loading.get(),
+ st::settingsBlockedHeightMin);
}
_controller->session().api().blockedPeers().slice(
@@ -77,7 +80,7 @@ QPointer Blocked::createPinnedToTop(not_null parent) {
content,
tr::lng_blocked_list_add(),
st::settingsButtonActive,
- { &st::menuIconBlockSettings, IconType::Round, &st::transparent }
+ { &st::menuIconBlockSettings }
)->addClickHandler([=] {
BlockedBoxController::BlockNewPeer(_controller);
});
@@ -201,7 +204,30 @@ void Blocked::setupContent() {
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) {
diff --git a/Telegram/SourceFiles/settings/settings_notifications.cpp b/Telegram/SourceFiles/settings/settings_notifications.cpp
index 9d4e1ed94..02240041f 100644
--- a/Telegram/SourceFiles/settings/settings_notifications.cpp
+++ b/Telegram/SourceFiles/settings/settings_notifications.cpp
@@ -8,8 +8,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "settings/settings_notifications.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/effects/animations.h"
+#include "ui/text/text_utilities.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/widgets/box_content_divider.h"
@@ -140,6 +143,160 @@ private:
};
+void AddTypeButton(
+ not_null container,
+ not_null