diff --git a/Telegram/Resources/icons/menu/mute_for_plain.png b/Telegram/Resources/icons/menu/mute_for_plain.png new file mode 100644 index 000000000..3c4a0db02 Binary files /dev/null and b/Telegram/Resources/icons/menu/mute_for_plain.png differ diff --git a/Telegram/Resources/icons/menu/mute_for_plain@2x.png b/Telegram/Resources/icons/menu/mute_for_plain@2x.png new file mode 100644 index 000000000..eeabd53e0 Binary files /dev/null and b/Telegram/Resources/icons/menu/mute_for_plain@2x.png differ diff --git a/Telegram/Resources/icons/menu/mute_for_plain@3x.png b/Telegram/Resources/icons/menu/mute_for_plain@3x.png new file mode 100644 index 000000000..ab53dceb5 Binary files /dev/null and b/Telegram/Resources/icons/menu/mute_for_plain@3x.png differ diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 78de0d456..7525ac789 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -891,6 +891,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_mute_menu_unmute" = "Unmute"; "lng_mute_menu_duration_hours#one" = "Mute for {count} hour"; "lng_mute_menu_duration_hours#other" = "Mute for {count} hours"; +"lng_mute_menu_duration_any" = "Mute for {duration}"; "lng_mute_menu_duration" = "Mute for..."; "lng_mute_menu_duration_forever" = "Mute forever"; "lng_mute_menu_duration_unmute" = "Unmute"; diff --git a/Telegram/SourceFiles/main/main_session_settings.cpp b/Telegram/SourceFiles/main/main_session_settings.cpp index 88a7fc041..81daee077 100644 --- a/Telegram/SourceFiles/main/main_session_settings.cpp +++ b/Telegram/SourceFiles/main/main_session_settings.cpp @@ -38,7 +38,9 @@ QByteArray SessionSettings::serialize() const { size += _groupStickersSectionHidden.size() * sizeof(quint64); size += _mediaLastPlaybackPosition.size() * 2 * sizeof(quint64); size += Serialize::bytearraySize(autoDownload); - size += sizeof(qint32) + _hiddenPinnedMessages.size() * (sizeof(quint64) + sizeof(qint32)); + size += sizeof(qint32) + + _hiddenPinnedMessages.size() * (sizeof(quint64) + sizeof(qint32)) + + (_mutePeriods.size() * sizeof(quint64)); auto result = QByteArray(); result.reserve(size); @@ -72,6 +74,10 @@ QByteArray SessionSettings::serialize() const { for (const auto &[key, value] : _hiddenPinnedMessages) { stream << SerializePeerId(key) << qint64(value.bare); } + stream << qint32(_mutePeriods.size()); + for (const auto &period : _mutePeriods) { + stream << quint64(period); + } } return result; } @@ -132,6 +138,7 @@ void SessionSettings::addFromSerialized(const QByteArray &serialized) { qint32 dialogsFiltersEnabled = _dialogsFiltersEnabled ? 1 : 0; qint32 supportAllSilent = _supportAllSilent ? 1 : 0; qint32 photoEditorHintShowsCount = _photoEditorHintShowsCount; + std::vector mutePeriods; stream >> versionTag; if (versionTag == kVersionTag) { @@ -351,6 +358,17 @@ void SessionSettings::addFromSerialized(const QByteArray &serialized) { } } } + if (!stream.atEnd()) { + auto count = qint32(0); + stream >> count; + if (stream.status() == QDataStream::Ok) { + for (auto i = 0; i != count; ++i) { + quint64 period; + stream >> period; + mutePeriods.emplace_back(period); + } + } + } if (stream.status() != QDataStream::Ok) { LOG(("App Error: " "Bad data for SessionSettings::addFromSerialized()")); @@ -394,6 +412,7 @@ void SessionSettings::addFromSerialized(const QByteArray &serialized) { _dialogsFiltersEnabled = (dialogsFiltersEnabled == 1); _supportAllSilent = (supportAllSilent == 1); _photoEditorHintShowsCount = std::move(photoEditorHintShowsCount); + _mutePeriods = std::move(mutePeriods); if (version < 2) { app.setLastSeenWarningSeen(appLastSeenWarningSeen == 1); @@ -542,4 +561,20 @@ void SessionSettings::incrementPhotoEditorHintShown() { } } +std::vector SessionSettings::mutePeriods() const { + return _mutePeriods; +} + +void SessionSettings::addMutePeriod(TimeId period) { + if (_mutePeriods.empty()) { + _mutePeriods.push_back(period); + } else if (_mutePeriods.back() != period) { + if (_mutePeriods.back() < period) { + _mutePeriods = { _mutePeriods.back(), period }; + } else { + _mutePeriods = { period, _mutePeriods.back() }; + } + } +} + } // namespace Main diff --git a/Telegram/SourceFiles/main/main_session_settings.h b/Telegram/SourceFiles/main/main_session_settings.h index 4e124275c..d4e2c55b5 100644 --- a/Telegram/SourceFiles/main/main_session_settings.h +++ b/Telegram/SourceFiles/main/main_session_settings.h @@ -123,6 +123,9 @@ public: [[nodiscard]] bool photoEditorHintShown() const; void incrementPhotoEditorHintShown(); + [[nodiscard]] std::vector mutePeriods() const; + void addMutePeriod(TimeId period); + private: static constexpr auto kDefaultSupportChatsLimitSlice = 7 * 24 * 60 * 60; static constexpr auto kPhotoEditorHintMaxShowsCount = 5; @@ -138,6 +141,7 @@ private: base::flat_map _hiddenPinnedMessages; bool _dialogsFiltersEnabled = false; int _photoEditorHintShowsCount = 0; + std::vector _mutePeriods; Support::SwitchSettings _supportSwitch; bool _supportFixChatsOrder = true; diff --git a/Telegram/SourceFiles/menu/menu_mute.cpp b/Telegram/SourceFiles/menu/menu_mute.cpp index dc10deb78..9b614c6ce 100644 --- a/Telegram/SourceFiles/menu/menu_mute.cpp +++ b/Telegram/SourceFiles/menu/menu_mute.cpp @@ -7,19 +7,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "menu/menu_mute.h" -#include "data/notify/data_notify_settings.h" #include "data/data_peer.h" #include "data/data_session.h" +#include "data/notify/data_notify_settings.h" #include "info/profile/info_profile_values.h" #include "lang/lang_keys.h" +#include "main/main_session.h" +#include "main/main_session_settings.h" #include "ui/boxes/choose_time.h" +#include "ui/boxes/time_picker_box.h" #include "ui/effects/animation_value.h" #include "ui/layers/generic_box.h" #include "ui/text/format_values.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/menu/menu_action.h" #include "ui/widgets/popup_menu.h" -#include "ui/boxes/time_picker_box.h" #include "styles/style_boxes.h" #include "styles/style_info.h" // infoTopBarMenu #include "styles/style_layers.h" @@ -31,6 +33,35 @@ namespace { constexpr auto kMuteDurSecondsDefault = crl::time(8) * 3600; +class IconWithText final : public Ui::Menu::Action { +public: + using Ui::Menu::Action::Action; + + void setData(const QString &text, const QPoint &iconPosition); + +protected: + void paintEvent(QPaintEvent *e) override; + +private: + QPoint _iconPosition; + QString _text; + +}; + +void IconWithText::setData(const QString &text, const QPoint &iconPosition) { + _iconPosition = iconPosition; + _text = text; +} + +void IconWithText::paintEvent(QPaintEvent *e) { + Ui::Menu::Action::paintEvent(e); + + Painter p(this); + p.setFont(st::menuIconMuteForAnyTextFont); + p.setPen(st::menuIconColor); + p.drawText(_iconPosition, _text); +} + class MuteItem final : public Ui::Menu::Action { public: MuteItem( @@ -161,9 +192,10 @@ void PickMuteBox(not_null box, not_null peer) { const auto pickerCallback = TimePickerBox(box, seconds, phrases, 0); box->addButton(tr::lng_mute_menu_mute(), [=] { - peer->owner().notifySettings().updateNotifySettings( - peer, - pickerCallback()); + const auto muteFor = pickerCallback(); + peer->owner().notifySettings().updateNotifySettings(peer, muteFor); + peer->session().settings().addMutePeriod(muteFor); + peer->session().saveSettings(); box->closeBox(); }); @@ -210,6 +242,32 @@ void FillMuteMenu( }, soundIsNone ? &st::menuIconSoundOn : &st::menuIconSoundOff); + const auto &st = menu->st().menu; + const auto iconTextPosition = st.itemIconPosition + + st::menuIconMuteForAnyTextPosition; + for (const auto &muteFor : peer->session().settings().mutePeriods()) { + const auto callback = [=] { + peer->owner().notifySettings().updateNotifySettings( + peer, + muteFor); + }; + + auto item = base::make_unique_q( + menu, + st, + Ui::Menu::CreateAction( + menu->menu().get(), + tr::lng_mute_menu_duration_any( + tr::now, + lt_duration, + Ui::FormatMuteFor(muteFor)), + callback), + &st::menuIconMuteForAny, + &st::menuIconMuteForAny); + item->setData(Ui::FormatMuteForTiny(muteFor), iconTextPosition); + menu->addAction(std::move(item)); + } + menu->addAction( tr::lng_mute_menu_duration(tr::now), [=, show = args.show] { show->showBox(Box(PickMuteBox, peer)); }, diff --git a/Telegram/SourceFiles/menu/menu_ttl.cpp b/Telegram/SourceFiles/menu/menu_ttl.cpp index e9ddbefa0..a8517b0e2 100644 --- a/Telegram/SourceFiles/menu/menu_ttl.cpp +++ b/Telegram/SourceFiles/menu/menu_ttl.cpp @@ -36,6 +36,7 @@ constexpr auto kTTLDurSeconds3 = kTTLDurHours3 * 3600; constexpr auto kTTLDurHours4 = crl::time(24 * 30); constexpr auto kTTLDurSeconds4 = kTTLDurHours4 * 3600; +// See menu_mute.cpp. class IconWithText final : public Ui::Menu::Action { public: using Ui::Menu::Action::Action; diff --git a/Telegram/SourceFiles/ui/menu_icons.style b/Telegram/SourceFiles/ui/menu_icons.style index 1535d7bb8..bf493d7e4 100644 --- a/Telegram/SourceFiles/ui/menu_icons.style +++ b/Telegram/SourceFiles/ui/menu_icons.style @@ -108,6 +108,10 @@ menuIconTTLAny: icon {{ "menu/auto_delete_plain", menuIconColor }}; menuIconTTLAnyTextPosition: point(11px, 22px); menuIconTTL: icon {{ "menu/auto_delete", menuIconColor }}; +menuIconMuteForAny: icon {{ "menu/mute_for_plain", menuIconColor }}; +menuIconMuteForAnyTextPosition: point(14px, 9px); +menuIconMuteForAnyTextFont: font(8px semibold); + mediaMenuIconStickers: icon {{ "menu/stickers", mediaviewMenuFg }}; mediaMenuIconCancel: icon {{ "menu/cancel", mediaviewMenuFg }}; mediaMenuIconShowInChat: icon {{ "menu/show_in_chat", mediaviewMenuFg }};