mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-18 07:07:08 +02:00
Add auto-delete period edit box.
This commit is contained in:
parent
dd2dcd7fd9
commit
15e03687f8
15 changed files with 525 additions and 147 deletions
Telegram
Resources/langs
SourceFiles
boxes
data
history/view/controls
media/view
ui
cmake
|
@ -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";
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<QString> title;
|
||||
std::optional<QString> description;
|
||||
std::optional<bool> hiddenPreHistory;
|
||||
std::optional<TimeId> messagesTTL;
|
||||
std::optional<bool> signatures;
|
||||
std::optional<ChannelData*> 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<void()> &&lambda);
|
||||
|
@ -377,7 +375,6 @@ private:
|
|||
void migrate(not_null<ChannelData*> channel);
|
||||
|
||||
std::optional<Privacy> _privacySavedValue;
|
||||
std::optional<TimeId> _ttlSavedValue;
|
||||
std::optional<ChannelData*> _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<QString>("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<rpl::event_stream<TimeId>>();
|
||||
|
||||
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<QString>("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::Saving> 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<ChannelData*> channel,
|
||||
bool hidden,
|
||||
|
@ -1615,41 +1580,6 @@ void Controller::deleteChannel() {
|
|||
} // namespace
|
||||
|
||||
|
||||
void AutoDeleteSettingsBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
TimeId ttlPeriod,
|
||||
Fn<void(TimeId)> 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<Window::SessionNavigation*> navigation,
|
||||
|
|
|
@ -23,11 +23,6 @@ class VerticalLayout;
|
|||
class SettingsButton;
|
||||
} // namespace Ui
|
||||
|
||||
void AutoDeleteSettingsBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
TimeId ttlPeriod,
|
||||
Fn<void(TimeId)> callback);
|
||||
|
||||
class EditPeerInfoBox : public Ui::BoxContent {
|
||||
public:
|
||||
EditPeerInfoBox(
|
||||
|
|
|
@ -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<
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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<PeerData*> 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<Ui::GenericBox*> box,
|
||||
not_null<PeerData*> peer) {
|
||||
struct State {
|
||||
TimeId savingPeriod = 0;
|
||||
bool savingOneSide = false;
|
||||
mtpRequestId savingRequestId = 0;
|
||||
QPointer<Ui::GenericBox> weak;
|
||||
};
|
||||
const auto state = std::make_shared<State>(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<QWidget*> parent, not_null<PeerData*> peer)
|
||||
: _button(parent, st::historyMessagesTTL) {
|
||||
|
@ -30,37 +119,14 @@ TTLButton::TTLButton(not_null<QWidget*> parent, not_null<PeerData*> 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([=] {
|
||||
|
|
|
@ -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<Ui::GenericBox*> box,
|
||||
not_null<PeerData*> peer);
|
||||
|
||||
class TTLButton final {
|
||||
public:
|
||||
TTLButton(not_null<QWidget*> parent, not_null<PeerData*> peer);
|
||||
|
|
|
@ -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()
|
||||
|
|
320
Telegram/SourceFiles/ui/boxes/auto_delete_settings.cpp
Normal file
320
Telegram/SourceFiles/ui/boxes/auto_delete_settings.cpp
Normal file
|
@ -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<Ui::RpWidget> CreateSliderForTTL(
|
||||
not_null<QWidget*> parent,
|
||||
std::vector<QString> labels,
|
||||
int dashedAfterIndex,
|
||||
int selected,
|
||||
Fn<void(int)> callback) {
|
||||
Expects(labels.size() > 1);
|
||||
Expects(selected >= 0 && selected < labels.size());
|
||||
Expects(dashedAfterIndex >= 0 && dashedAfterIndex < labels.size());
|
||||
|
||||
struct State {
|
||||
std::vector<int> points;
|
||||
std::vector<QString> 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<Ui::FixedHeightWidget>(parent.get(), height);
|
||||
const auto raw = result.data();
|
||||
const auto slider = Ui::CreateChild<Ui::FixedHeightWidget>(
|
||||
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>(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<qreal>{
|
||||
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<QEvent*> e) {
|
||||
return (e->type() == QEvent::MouseButtonPress)
|
||||
&& (static_cast<QMouseEvent*>(e.get())->button()
|
||||
== Qt::LeftButton)
|
||||
&& (state->points[1] > 0);
|
||||
}) | rpl::map([=](not_null<QEvent*> e) {
|
||||
return rpl::single(
|
||||
static_cast<QMouseEvent*>(e.get())->pos()
|
||||
) | rpl::then(slider->events(
|
||||
) | rpl::take_while([=](not_null<QEvent*> e) {
|
||||
return (e->type() != QEvent::MouseButtonRelease)
|
||||
|| (static_cast<QMouseEvent*>(e.get())->button()
|
||||
!= Qt::LeftButton);
|
||||
}) | rpl::filter([=](not_null<QEvent*> e) {
|
||||
return (e->type() == QEvent::MouseMove);
|
||||
}) | rpl::map([=](not_null<QEvent*> e) {
|
||||
return static_cast<QMouseEvent*>(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<Ui::GenericBox*> box,
|
||||
TimeId ttlMyPeriod,
|
||||
TimeId ttlPeerPeriod,
|
||||
bool ttlOneSide,
|
||||
std::optional<QString> userFirstName,
|
||||
Fn<void(TimeId, bool)> callback) {
|
||||
box->setTitle(tr::lng_manage_messages_ttl_title());
|
||||
box->setWidth(st::boxWideWidth);
|
||||
|
||||
struct State {
|
||||
TimeId my = 0;
|
||||
bool oneSide = false;
|
||||
rpl::event_stream<rpl::producer<QString>> aboutTexts;
|
||||
Fn<void()> update;
|
||||
};
|
||||
|
||||
const auto state = box->lifetime().make_state<State>(State{
|
||||
.my = ttlMyPeriod,
|
||||
.oneSide = ttlOneSide,
|
||||
});
|
||||
|
||||
const auto options = std::vector<QString>{
|
||||
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<Ui::Checkbox>(
|
||||
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<Ui::DividerLabel>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
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
|
22
Telegram/SourceFiles/ui/boxes/auto_delete_settings.h
Normal file
22
Telegram/SourceFiles/ui/boxes/auto_delete_settings.h
Normal file
|
@ -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<Ui::GenericBox*> box,
|
||||
TimeId ttlMyPeriod,
|
||||
TimeId ttlPeerPeriod,
|
||||
bool ttlOneSide,
|
||||
std::optional<QString> userFirstName,
|
||||
Fn<void(TimeId, bool)> callback);
|
||||
|
||||
} // namespace Ui
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace Ui {
|
|||
|
||||
struct MultilineToastArgs {
|
||||
TextWithEntities text;
|
||||
crl::time duration = 0;
|
||||
};
|
||||
|
||||
void ShowMultilineToast(MultilineToastArgs &&args);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue