From 15e03687f87d1ee8fdb5c954d2592a4f93950785 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 11 Feb 2021 18:46:46 +0400 Subject: [PATCH] Add auto-delete period edit box. --- Telegram/Resources/langs/lang.strings | 13 +- Telegram/SourceFiles/boxes/confirm_box.cpp | 22 +- .../boxes/peers/edit_peer_info_box.cpp | 110 ++---- .../boxes/peers/edit_peer_info_box.h | 5 - .../boxes/peers/edit_peer_invite_links.cpp | 1 + Telegram/SourceFiles/data/data_peer.cpp | 2 +- .../view/controls/history_view_ttl_button.cpp | 120 +++++-- .../view/controls/history_view_ttl_button.h | 8 + .../view/media_view_playback_controls.cpp | 16 +- .../ui/boxes/auto_delete_settings.cpp | 320 ++++++++++++++++++ .../ui/boxes/auto_delete_settings.h | 22 ++ Telegram/SourceFiles/ui/chat/chat.style | 27 ++ .../SourceFiles/ui/toasts/common_toasts.cpp | 3 + .../SourceFiles/ui/toasts/common_toasts.h | 1 + Telegram/cmake/td_ui.cmake | 2 + 15 files changed, 525 insertions(+), 147 deletions(-) create mode 100644 Telegram/SourceFiles/ui/boxes/auto_delete_settings.cpp create mode 100644 Telegram/SourceFiles/ui/boxes/auto_delete_settings.h diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 7af4eeaf7..2f9016e49 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -982,7 +982,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_manage_messages_ttl_after2" = "After 7 days"; "lng_manage_messages_ttl_about" = "Turning on this setting will make auto-delete messages from this group after the selected period."; "lng_manage_messages_ttl_about_channel" = "Turning on this setting will make auto-delete messages from this channel after the selected period."; + +"lng_ttl_edit_title" = "Auto-delete messages in this chat"; +"lng_ttl_edit_about" = "Automatically delete new messages sent in this chat after a certain period of time."; +"lng_ttl_edit_about_other" = "{user} has set messages to auto-delete in {duration} for both of you."; +"lng_ttl_edit_about_you" = "You have set messages to auto-delete in {duration} for both you and {user}."; +"lng_ttl_edit_about_you_only" = "You have set messages to auto-delete in {duration} only for yourself."; +"lng_ttl_also_checkbox" = "Also delete for {user}"; +"lng_ttl_about_tooltip_on_title" = "Auto-delete On – {duration}"; "lng_ttl_about_tooltip" = "Messages in this chat will auto-delete in {duration}."; +"lng_ttl_about_tooltip_no_longer" = "{user} has set messages to auto-delete in {duration}. You can't make this interval longer."; +"lng_ttl_about_tooltip_no_cancel" = "{user} has set messages to auto-delete in {duration}. You can't cancel this."; +"lng_ttl_about_tooltip_off" = "Auto-delete is now Off."; "lng_ttl_about_duration1" = "24 hours"; "lng_ttl_about_duration2" = "7 days"; @@ -1750,7 +1761,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_mediaview_saved_to" = "Image was saved to your {downloads} folder"; "lng_mediaview_downloads" = "Downloads"; "lng_mediaview_video_loading" = "Loading - {percent}"; -"lng_mediaview_playback_speed" = "Playback speed"; +"lng_mediaview_playback_speed" = "Playback speed: {speed}"; "lng_mediaview_rotate_video" = "Rotate video"; "lng_theme_preview_title" = "Theme Preview"; diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index e560fe0b2..5ae466802 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -37,7 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_photo_media.h" #include "data/data_changes.h" #include "base/unixtime.h" -#include "boxes/peers/edit_peer_info_box.h" +#include "history/view/controls/history_view_ttl_button.h" #include "main/main_session.h" #include "mtproto/mtproto_config.h" #include "facades.h" // Ui::showChatsList @@ -657,28 +657,16 @@ void DeleteMessagesBox::prepare() { && (_wipeHistoryPeer->isUser() || _wipeHistoryPeer->isMegagroup() || _wipeHistoryPeer->isChat())) { + _wipeHistoryPeer->updateFull(); _autoDeleteSettings.create( this, tr::lng_edit_auto_delete_settings(tr::now), st::boxLinkButton); - const auto peer = _wipeHistoryPeer; - const auto callback = crl::guard(&peer->session(), [=](TimeId period) { - using Flag = MTPmessages_SetHistoryTTL::Flag; - peer->session().api().request(MTPmessages_SetHistoryTTL( - MTP_flags(peer->oneSideTTL() ? Flag::f_pm_oneside : Flag(0)), - peer->input, - MTP_int(period) - )).done([=](const MTPUpdates &result) { - peer->session().api().applyUpdates(result); - }).fail([=](const RPCError &error) { - }).send(); - }); _autoDeleteSettings->setClickedCallback([=] { getDelegate()->show( Box( - AutoDeleteSettingsBox, - _wipeHistoryPeer->myMessagesTTL(), - callback), + HistoryView::Controls::AutoDeleteSettingsBox, + _wipeHistoryPeer), Ui::LayerOption(0)); }); } @@ -703,7 +691,7 @@ void DeleteMessagesBox::prepare() { fullHeight += st::boxMediumSkip + _revoke->heightNoMargins(); } if (_autoDeleteSettings) { - fullHeight += st::boxMediumSkip + _autoDeleteSettings->height(); + fullHeight += st::boxMediumSkip + _autoDeleteSettings->height() + st::boxLittleSkip; } setDimensions(st::boxWidth, fullHeight); } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index c0ae442d4..d5924277f 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_changes.h" #include "history/admin_log/history_admin_log_section.h" +#include "history/view/controls/history_view_ttl_button.h" #include "info/profile/info_profile_values.h" #include "lang/lang_keys.h" #include "mainwidget.h" @@ -315,7 +316,6 @@ private: std::optional title; std::optional description; std::optional hiddenPreHistory; - std::optional messagesTTL; std::optional signatures; std::optional linkedChat; }; @@ -351,7 +351,6 @@ private: bool validateTitle(Saving &to) const; bool validateDescription(Saving &to) const; bool validateHistoryVisibility(Saving &to) const; - bool validateMessagesTTL(Saving &to) const; bool validateSignatures(Saving &to) const; void save(); @@ -360,7 +359,6 @@ private: void saveTitle(); void saveDescription(); void saveHistoryVisibility(); - void saveMessagesTTL(); void saveSignatures(); void savePhoto(); void pushSaveStage(FnMut &&lambda); @@ -377,7 +375,6 @@ private: void migrate(not_null channel); std::optional _privacySavedValue; - std::optional _ttlSavedValue; std::optional _linkedChatSavedValue; ChannelData *_linkedChatOriginalValue = nullptr; bool _channelHasLocationOriginalValue = false; @@ -889,36 +886,30 @@ void Controller::fillHistoryVisibilityButton() { void Controller::fillSetMessagesTTLButton() { Expects(_controls.buttonsLayout != nullptr); - _ttlSavedValue = _peer->messagesTTL(); + auto label = _peer->session().changes().peerFlagsValue( + _peer, + Data::PeerUpdate::Flag::MessagesTTL + ) | rpl::map([=] { + const auto period = _peer->messagesTTL(); + return !period + ? tr::lng_manage_messages_ttl_never() + : (period == 5) // for debugging + ? rpl::single("5 seconds") // for debugging + : (period < 3 * 86400) + ? tr::lng_manage_messages_ttl_after1() + : tr::lng_manage_messages_ttl_after2(); + }) | rpl::flatten_latest(); - const auto updateMessagesTTL = - std::make_shared>(); - - const auto boxCallback = crl::guard(this, [=](TimeId value) { - updateMessagesTTL->fire_copy(value); - _ttlSavedValue = value; - }); const auto buttonCallback = [=] { Ui::show( - Box(AutoDeleteSettingsBox, *_ttlSavedValue, boxCallback), + Box(HistoryView::Controls::AutoDeleteSettingsBox, _peer), Ui::LayerOption::KeepOther); }; AddButtonWithText( _controls.buttonsLayout, tr::lng_manage_messages_ttl_title(), - updateMessagesTTL->events( - ) | rpl::map([](TimeId value) { - return !value - ? tr::lng_manage_messages_ttl_never() - : (value == 5) AssertIsDebug() - ? rpl::single("5 seconds") AssertIsDebug() - : (value < 3 * 86400) - ? tr::lng_manage_messages_ttl_after1() - : tr::lng_manage_messages_ttl_after2(); - }) | rpl::flatten_latest(), + std::move(label), buttonCallback); - - updateMessagesTTL->fire_copy(*_ttlSavedValue); } void Controller::fillManageSection() { @@ -945,9 +936,11 @@ void Controller::fillManageSection() { : chat->canEditPreHistoryHidden(); }(); const auto canSetMessagesTTL = [&] { + // Leave this entry point only for channels for now. + // Groups and users have their entry point in 'Clear History' box. return isChannel - ? channel->canDeleteMessages() - : chat->canDeleteMessages(); + && !channel->isMegagroup() + && channel->canDeleteMessages(); }(); const auto canEditPermissions = [&] { @@ -1184,7 +1177,6 @@ std::optional Controller::validate() const { && validateTitle(result) && validateDescription(result) && validateHistoryVisibility(result) - && validateMessagesTTL(result) && validateSignatures(result)) { return result; } @@ -1252,14 +1244,6 @@ bool Controller::validateHistoryVisibility(Saving &to) const { return true; } -bool Controller::validateMessagesTTL(Saving &to) const { - if (!_ttlSavedValue) { - return true; - } - to.messagesTTL = _ttlSavedValue; - return true; -} - bool Controller::validateSignatures(Saving &to) const { if (!_signaturesSavedValue.has_value()) { return true; @@ -1281,7 +1265,6 @@ void Controller::save() { pushSaveStage([=] { saveTitle(); }); pushSaveStage([=] { saveDescription(); }); pushSaveStage([=] { saveHistoryVisibility(); }); - pushSaveStage([=] { saveMessagesTTL(); }); pushSaveStage([=] { saveSignatures(); }); pushSaveStage([=] { savePhoto(); }); continueSave(); @@ -1489,24 +1472,6 @@ void Controller::saveHistoryVisibility() { [=] { cancelSave(); }); } -void Controller::saveMessagesTTL() { - if (!_savingData.messagesTTL - || *_savingData.messagesTTL == _peer->messagesTTL()) { - return continueSave(); - } - using Flag = MTPmessages_SetHistoryTTL::Flag; - _api.request(MTPmessages_SetHistoryTTL( - MTP_flags(_peer->oneSideTTL() ? Flag::f_pm_oneside : Flag(0)), - _peer->input, - MTP_int(*_savingData.messagesTTL) - )).done([=](const MTPUpdates &result) { - _peer->session().api().applyUpdates(result); - continueSave(); - }).fail([=](const RPCError &error) { - cancelSave(); - }).send(); -} - void Controller::togglePreHistoryHidden( not_null channel, bool hidden, @@ -1615,41 +1580,6 @@ void Controller::deleteChannel() { } // namespace -void AutoDeleteSettingsBox( - not_null box, - TimeId ttlPeriod, - Fn callback) { - const auto options = { - tr::lng_manage_messages_ttl_never(tr::now), - tr::lng_manage_messages_ttl_after1(tr::now), - tr::lng_manage_messages_ttl_after2(tr::now), - u"5 seconds"_q, AssertIsDebug() - }; - const auto initial = !ttlPeriod - ? 0 - : (ttlPeriod == 5) AssertIsDebug() - ? 3 AssertIsDebug() - : (ttlPeriod < 3 * 86400) - ? 1 - : 2; - const auto callbackFromOption = [=](int option) { - const auto period = !option - ? 0 - : (option == 1) - ? 86400 - : (option == 3) AssertIsDebug() - ? 5 AssertIsDebug() - : 7 * 86400; - callback(period); - }; - SingleChoiceBox(box, { - .title = tr::lng_manage_messages_ttl_title(), - .options = options, - .initialSelection = initial, - .callback = callbackFromOption, - }); -} - EditPeerInfoBox::EditPeerInfoBox( QWidget*, not_null navigation, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h index 3a81e6fa1..11ffd36ec 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.h @@ -23,11 +23,6 @@ class VerticalLayout; class SettingsButton; } // namespace Ui -void AutoDeleteSettingsBox( - not_null box, - TimeId ttlPeriod, - Fn callback); - class EditPeerInfoBox : public Ui::BoxContent { public: EditPeerInfoBox( diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp index 8062305ba..017a1ae84 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_links.cpp @@ -858,6 +858,7 @@ void ManageInviteLinksBox( using namespace Settings; box->setTitle(tr::lng_group_invite_title()); + box->setWidth(st::boxWideWidth); const auto container = box->verticalLayout(); const auto permanentFromList = box->lifetime().make_state< diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 8d3448043..0f43b1100 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -971,7 +971,7 @@ void PeerData::applyMessagesTTL(const MTPPeerHistoryTTL &ttl) { ttl.match([&](const MTPDpeerHistoryTTL &data) { setMessagesTTL( data.vttl_period().v, - data.vttl_period().v, + 0, false); }, [&](const MTPDpeerHistoryTTLPM &data) { setMessagesTTL( diff --git a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp index 646dee793..4990955f3 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp @@ -14,12 +14,101 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "lang/lang_keys.h" #include "boxes/peers/edit_peer_info_box.h" -#include "ui/layers/generic_box.h" +#include "ui/boxes/auto_delete_settings.h" #include "ui/toast/toast.h" +#include "ui/toasts/common_toasts.h" +#include "ui/text/text_utilities.h" #include "apiwrap.h" #include "styles/style_chat.h" namespace HistoryView::Controls { +namespace { + +constexpr auto kToastDuration = crl::time(3500); + +} // namespace + +void ShowAutoDeleteToast(not_null peer) { + const auto period = peer->messagesTTL(); + if (!period) { + Ui::Toast::Show(tr::lng_ttl_about_tooltip_off(tr::now)); + return; + } + + const auto duration = (period == 5) + ? u"5 seconds"_q + : (period < 3 * 86400) + ? tr::lng_ttl_about_duration1(tr::now) + : tr::lng_ttl_about_duration2(tr::now); + auto rich = Ui::Text::Bold( + tr::lng_ttl_about_tooltip_on_title(tr::now, lt_duration, duration) + ).append('\n'); + + const auto myPeriod = peer->myMessagesTTL(); + rich.append((period == myPeriod) + ? tr::lng_ttl_about_tooltip(tr::now, lt_duration, duration) + : (myPeriod + ? tr::lng_ttl_about_tooltip_no_longer + : tr::lng_ttl_about_tooltip_no_cancel)( + tr::now, + lt_user, + peer->shortName(), + lt_duration, + duration)); + Ui::ShowMultilineToast({ + .text = std::move(rich), + .duration = kToastDuration, + }); +} + +void AutoDeleteSettingsBox( + not_null box, + not_null peer) { + struct State { + TimeId savingPeriod = 0; + bool savingOneSide = false; + mtpRequestId savingRequestId = 0; + QPointer weak; + }; + const auto state = std::make_shared(State{ .weak = box.get() }); + auto callback = [=](TimeId period, bool oneSide) { + auto &api = peer->session().api(); + if (state->savingRequestId) { + if (period == state->savingPeriod + && oneSide == state->savingOneSide) { + return; + } + api.request(state->savingRequestId).cancel(); + } + state->savingPeriod = period; + state->savingOneSide = oneSide; + using Flag = MTPmessages_SetHistoryTTL::Flag; + state->savingRequestId = api.request(MTPmessages_SetHistoryTTL( + MTP_flags((oneSide && peer->isUser()) + ? Flag::f_pm_oneside + : Flag(0)), + peer->input, + MTP_int(period) + )).done([=](const MTPUpdates &result) { + peer->session().api().applyUpdates(result); + ShowAutoDeleteToast(peer); + if (const auto strong = state->weak.data()) { + strong->closeBox(); + } + }).fail([=](const RPCError &error) { + state->savingRequestId = 0; + }).send(); + }; + Ui::AutoDeleteSettingsBox( + box, + peer->myMessagesTTL(), + peer->peerMessagesTTL(), + peer->oneSideTTL(), + (peer->isUser() + ? std::make_optional(peer->shortName()) + : std::nullopt), + std::move(callback)); +} TTLButton::TTLButton(not_null parent, not_null peer) : _button(parent, st::historyMessagesTTL) { @@ -30,37 +119,14 @@ TTLButton::TTLButton(not_null parent, not_null peer) || (peer->isChannel() && peer->asChannel()->canDeleteMessages()); if (!canEdit) { - const auto duration = (peer->messagesTTL() < 3 * 86400) - ? tr::lng_ttl_about_duration1(tr::now) - : tr::lng_ttl_about_duration2(tr::now); - Ui::Toast::Show(tr::lng_ttl_about_tooltip( - tr::now, - lt_duration, - duration)); + ShowAutoDeleteToast(peer); return; } - const auto callback = crl::guard(&peer->session(), [=]( - TimeId period) { - using Flag = MTPmessages_SetHistoryTTL::Flag; - peer->session().api().request(MTPmessages_SetHistoryTTL( - MTP_flags(peer->oneSideTTL() - ? Flag::f_pm_oneside - : Flag(0)), - peer->input, - MTP_int(period) - )).done([=](const MTPUpdates &result) { - peer->session().api().applyUpdates(result); - }).fail([=](const RPCError &error) { - }).send(); - }); Ui::show( - Box( - AutoDeleteSettingsBox, - peer->myMessagesTTL(), - callback), + Box(AutoDeleteSettingsBox, peer), Ui::LayerOption(0)); }); - peer->session().changes().peerUpdates( + peer->session().changes().peerFlagsValue( peer, Data::PeerUpdate::Flag::MessagesTTL ) | rpl::start_with_next([=] { diff --git a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.h b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.h index 37dda0141..f1dc37f22 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.h @@ -9,8 +9,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" +namespace Ui { +class GenericBox; +} // namespace Ui + namespace HistoryView::Controls { +void AutoDeleteSettingsBox( + not_null box, + not_null peer); + class TTLButton final { public: TTLButton(not_null parent, not_null peer); diff --git a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp index 89f1e8c26..16d06deb5 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp +++ b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp @@ -99,18 +99,22 @@ MenuSpeedItem::MenuSpeedItem( return anim::interpolate(kMinSpeed, kMaxSpeed, value) / 100.; }; const auto speedString = [=](float64 value) { - return u"%1: %2x"_q - .arg(tr::lng_mediaview_playback_speed(tr::now)) - .arg(QString::number(computeSpeed(value), 'f', 2)); + return tr::lng_mediaview_playback_speed( + tr::now, + lt_speed, + QString::number(computeSpeed(value), 'f', 2) + 'x'); }; _slider->setAlwaysDisplayMarker(true); _slider->setValue((std::round(startSpeed * 100.) - kMinSpeed) / (kMaxSpeed - kMinSpeed)); - _slider->addDivider( - kSpeedStickedValues[1].first, - st::speedSliderDividerSize); + for (const auto &sticked : kSpeedStickedValues) { + _slider->addDivider(sticked.first, st::speedSliderDividerSize); + } + //_slider->addDivider( + // kSpeedStickedValues[1].first, + // st::speedSliderDividerSize); { const auto goodWidth = st.itemPadding.left() diff --git a/Telegram/SourceFiles/ui/boxes/auto_delete_settings.cpp b/Telegram/SourceFiles/ui/boxes/auto_delete_settings.cpp new file mode 100644 index 000000000..c2234ad97 --- /dev/null +++ b/Telegram/SourceFiles/ui/boxes/auto_delete_settings.cpp @@ -0,0 +1,320 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "ui/boxes/auto_delete_settings.h" + +#include "ui/widgets/checkbox.h" +#include "lang/lang_keys.h" +#include "styles/style_chat.h" +#include "styles/style_layers.h" + +namespace Ui { +namespace { + +object_ptr CreateSliderForTTL( + not_null parent, + std::vector labels, + int dashedAfterIndex, + int selected, + Fn callback) { + Expects(labels.size() > 1); + Expects(selected >= 0 && selected < labels.size()); + Expects(dashedAfterIndex >= 0 && dashedAfterIndex < labels.size()); + + struct State { + std::vector points; + std::vector labels; + int selected = 0; + }; + static const auto st = &st::defaultSliderForTTL; + const auto height = st->font->height + st->skip + st->chosenSize; + const auto count = int(labels.size()); + + auto result = object_ptr(parent.get(), height); + const auto raw = result.data(); + const auto slider = Ui::CreateChild( + raw, + st->chosenSize); + slider->setCursor(style::cur_pointer); + slider->move(0, height - slider->height()); + + auto &lifetime = raw->lifetime(); + const auto state = lifetime.make_state(State{ + .labels = std::move(labels), + .selected = selected + }); + state->points.resize(count, 0); + + raw->widthValue( + ) | rpl::start_with_next([=](int width) { + for (auto i = 0; i != count; ++i) { + state->points[i] = (width * i) / (count - 1); + } + slider->resize(width, slider->height()); + }, lifetime); + + raw->paintRequest( + ) | rpl::start_with_next([=] { + auto p = QPainter(raw); + + p.setFont(st->font); + for (auto i = 0; i != count; ++i) { + // Label + p.setPen(st->textFg); + const auto &text = state->labels[i]; + const auto textWidth = st->font->width(text); + const auto shift = (i == count - 1) + ? textWidth + : (i > 0) + ? (textWidth / 2) + : 0; + const auto x = state->points[i] - shift; + const auto y = st->font->ascent; + p.drawText(x, y, text); + } + }, lifetime); + + slider->paintRequest( + ) | rpl::start_with_next([=] { + auto p = QPainter(slider); + auto hq = PainterHighQualityEnabler(p); + + p.setFont(st->font); + for (auto i = 0; i != count; ++i) { + const auto middle = (st->chosenSize / 2.); + + // Point + const auto size = (i == state->selected) + ? st->chosenSize + : st->pointSize; + const auto pointfg = (i <= state->selected) + ? st->activeFg + : st->inactiveFg; + const auto shift = (i == count - 1) + ? float64(size) + : (i > 0) + ? (size / 2.) + : 0.; + const auto pointx = state->points[i] - shift; + const auto pointy = middle - (size / 2.); + + p.setPen(Qt::NoPen); + p.setBrush(pointfg); + p.drawEllipse(QRectF{ pointx, pointy, size * 1., size * 1. }); + + // Line + if (i + 1 == count) { + break; + } + const auto nextSize = (i + 1 == state->selected) + ? st->chosenSize + : st->pointSize; + const auto nextShift = (i + 1 == count - 1) + ? float64(nextSize) + : (nextSize / 2.); + const auto &linefg = (i + 1 <= state->selected) + ? st->activeFg + : st->inactiveFg; + const auto from = pointx + size + st->stroke * 1.5; + const auto till = state->points[i + 1] - nextShift - st->stroke * 1.5; + + auto pen = linefg->p; + pen.setWidthF(st->stroke); + if (i >= dashedAfterIndex) { + // Try to fill the line with exact number of dash segments. + // UPD Doesn't work so well because it changes when clicking. + //const auto length = till - from; + //const auto offSegmentsCount = int(std::round( + // (length - st->dashOn) / (st->dashOn + st->dashOff))); + //const auto onSegmentsCount = offSegmentsCount + 1; + //const auto idealLength = offSegmentsCount * st->dashOff + // + onSegmentsCount * st->dashOn; + //const auto multiplier = length / float64(idealLength); + + const auto multiplier = 1.; + auto dashPattern = QVector{ + st->dashOn * multiplier / st->stroke, + st->dashOff * multiplier / st->stroke + }; + pen.setDashPattern(dashPattern); + } + pen.setCapStyle(Qt::RoundCap); + p.setPen(pen); + + p.setBrush(Qt::NoBrush); + p.drawLine(QPointF(from, middle), QPointF(till, middle)); + } + }, lifetime); + + slider->events( + ) | rpl::filter([=](not_null e) { + return (e->type() == QEvent::MouseButtonPress) + && (static_cast(e.get())->button() + == Qt::LeftButton) + && (state->points[1] > 0); + }) | rpl::map([=](not_null e) { + return rpl::single( + static_cast(e.get())->pos() + ) | rpl::then(slider->events( + ) | rpl::take_while([=](not_null e) { + return (e->type() != QEvent::MouseButtonRelease) + || (static_cast(e.get())->button() + != Qt::LeftButton); + }) | rpl::filter([=](not_null e) { + return (e->type() == QEvent::MouseMove); + }) | rpl::map([=](not_null e) { + return static_cast(e.get())->pos(); + })); + }) | rpl::flatten_latest( + ) | rpl::start_with_next([=](QPoint position) { + state->selected = std::clamp( + (position.x() + (state->points[1] / 2)) / state->points[1], + 0, + count - 1); + slider->update(); + callback(state->selected); + }, lifetime); + + return result; +} + +} // namespace + +void AutoDeleteSettingsBox( + not_null box, + TimeId ttlMyPeriod, + TimeId ttlPeerPeriod, + bool ttlOneSide, + std::optional userFirstName, + Fn callback) { + box->setTitle(tr::lng_manage_messages_ttl_title()); + box->setWidth(st::boxWideWidth); + + struct State { + TimeId my = 0; + bool oneSide = false; + rpl::event_stream> aboutTexts; + Fn update; + }; + + const auto state = box->lifetime().make_state(State{ + .my = ttlMyPeriod, + .oneSide = ttlOneSide, + }); + + const auto options = std::vector{ + u"5 seconds"_q, AssertIsDebug() + tr::lng_manage_messages_ttl_after1(tr::now), + tr::lng_manage_messages_ttl_after2(tr::now), + tr::lng_manage_messages_ttl_never(tr::now), + }; + const auto periodToIndex = [&](TimeId period) { + return !period + ? 3 + : (period == 5) AssertIsDebug() + ? 0 AssertIsDebug() + : (period < 3 * 86400) + ? 1 + : 2; + }; + const auto indexToPeriod = [&](int index) { + return !index + ? 5 AssertIsDebug() + : (index == 1) AssertIsDebug() + ? 86400 + : (index == 2) + ? 7 * 86400 + : 0; + }; + const auto sliderCallback = [=](int index) { + state->my = indexToPeriod(index); + state->update(); + }; + const auto slider = box->addRow( + CreateSliderForTTL( + box, + options | ranges::to_vector, + periodToIndex(ttlPeerPeriod), + periodToIndex(ttlMyPeriod), + sliderCallback), + { + st::boxRowPadding.left(), + 0, + st::boxRowPadding.right(), + st::boxMediumSkip }); + + const auto bothSides = userFirstName + ? box->addRow( + object_ptr( + box, + tr::lng_ttl_also_checkbox(tr::now, lt_user, *userFirstName), + !ttlOneSide), + { + st::boxRowPadding.left(), + 0, + st::boxRowPadding.right(), + st::boxMediumSkip }) + : nullptr; + + const auto description = box->addRow( + object_ptr( + box, + object_ptr( + box, + state->aboutTexts.events() | rpl::flatten_latest(), + st::boxDividerLabel), + st::ttlDividerLabelPadding), + style::margins()); + + if (bothSides) { + bothSides->checkedChanges( + ) | rpl::start_with_next([=](bool checked) { + state->oneSide = !checked; + state->update(); + }, bothSides->lifetime()); + } + + state->update = [=] { + const auto his = ttlPeerPeriod; + const auto wrap = [](TimeId period) { + Expects(period > 0); + + return (period == 5) AssertIsDebug() + ? rpl::single(u"5 seconds"_q) AssertIsDebug() + : (period < 3 * 86400) + ? tr::lng_ttl_about_duration1() + : tr::lng_ttl_about_duration2(); + }; + state->aboutTexts.fire(((!state->my && !his) || !userFirstName) + ? tr::lng_ttl_edit_about() + : (his > 0 && (!state->my || his < state->my)) + ? tr::lng_ttl_edit_about_other( + lt_user, + rpl::single(*userFirstName), + lt_duration, + wrap(his)) + : state->oneSide + ? tr::lng_ttl_edit_about_you_only(lt_duration, wrap(state->my)) + : tr::lng_ttl_edit_about_you( + lt_duration, + wrap(state->my), + lt_user, + rpl::single(*userFirstName))); + }; + state->update(); + + box->addButton(tr::lng_settings_save(), [=] { + const auto period = state->my; + const auto oneSide = state->oneSide; + box->closeBox(); + + callback(period, oneSide); + }); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/boxes/auto_delete_settings.h b/Telegram/SourceFiles/ui/boxes/auto_delete_settings.h new file mode 100644 index 000000000..0422fa446 --- /dev/null +++ b/Telegram/SourceFiles/ui/boxes/auto_delete_settings.h @@ -0,0 +1,22 @@ +/* +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 "ui/layers/generic_box.h" + +namespace Ui { + +void AutoDeleteSettingsBox( + not_null box, + TimeId ttlMyPeriod, + TimeId ttlPeerPeriod, + bool ttlOneSide, + std::optional userFirstName, + Fn callback); + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 2dec590e4..dd43299bf 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -850,3 +850,30 @@ videoIcon: icon { { "media_video_play_bg", videoPlayIconBg }, { "media_video_play", videoPlayIconFg, point(12px, 12px) }, }; + +SliderForTTL { + font: font; + textFg: color; + pointSize: pixels; + chosenSize: pixels; + skip: pixels; + stroke: pixels; + activeFg: color; + inactiveFg: color; + dashOn: pixels; + dashOff: pixels; +} + +defaultSliderForTTL: SliderForTTL { + font: normalFont; + textFg: windowSubTextFg; + pointSize: 6px; + chosenSize: 12px; + skip: 8px; + stroke: 2px; + activeFg: mediaPlayerActiveFg; + inactiveFg: mediaPlayerInactiveFg; + dashOn: 8px; + dashOff: 5px; +} +ttlDividerLabelPadding: margins(22px, 10px, 22px, 19px); diff --git a/Telegram/SourceFiles/ui/toasts/common_toasts.cpp b/Telegram/SourceFiles/ui/toasts/common_toasts.cpp index ad68b758c..87de13737 100644 --- a/Telegram/SourceFiles/ui/toasts/common_toasts.cpp +++ b/Telegram/SourceFiles/ui/toasts/common_toasts.cpp @@ -16,6 +16,9 @@ void ShowMultilineToast(MultilineToastArgs &&args) { Ui::Toast::Show(Ui::Toast::Config{ .text = std::move(args.text), .st = &st::defaultMultilineToast, + .durationMs = (args.duration + ? args.duration + : Ui::Toast::kDefaultDuration), .multiline = true, }); } diff --git a/Telegram/SourceFiles/ui/toasts/common_toasts.h b/Telegram/SourceFiles/ui/toasts/common_toasts.h index ea2fe9282..820e486ff 100644 --- a/Telegram/SourceFiles/ui/toasts/common_toasts.h +++ b/Telegram/SourceFiles/ui/toasts/common_toasts.h @@ -13,6 +13,7 @@ namespace Ui { struct MultilineToastArgs { TextWithEntities text; + crl::time duration = 0; }; void ShowMultilineToast(MultilineToastArgs &&args); diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index 0c9020a3a..73528d4dc 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -64,6 +64,8 @@ PRIVATE platform/mac/file_bookmark_mac.mm platform/platform_file_bookmark.h + ui/boxes/auto_delete_settings.cpp + ui/boxes/auto_delete_settings.h ui/boxes/calendar_box.cpp ui/boxes/calendar_box.h ui/boxes/choose_date_time.cpp