mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-06 15:13:57 +02:00
Added ability to set emoji status for certain period of time.
This commit is contained in:
parent
9bb2bb09b9
commit
fa4801ac94
10 changed files with 166 additions and 47 deletions
|
@ -1811,6 +1811,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_accounts_limit1#other" = "You have reached the limit of **{count}** connected accounts.";
|
"lng_accounts_limit1#other" = "You have reached the limit of **{count}** connected accounts.";
|
||||||
"lng_accounts_limit2" = "You can free one space by subscribing to **Telegram Premium** with one of these connected accounts:";
|
"lng_accounts_limit2" = "You can free one space by subscribing to **Telegram Premium** with one of these connected accounts:";
|
||||||
|
|
||||||
|
"lng_emoji_status_for_title" = "Set Status for...";
|
||||||
|
"lng_emoji_status_for_submit" = "Set Status";
|
||||||
|
"lng_emoji_status_menu_duration_any" = "Set Status for {duration}";
|
||||||
|
|
||||||
"lng_group_about_header" = "You have created a group.";
|
"lng_group_about_header" = "You have created a group.";
|
||||||
"lng_group_about_text" = "Groups can have:";
|
"lng_group_about_text" = "Groups can have:";
|
||||||
"lng_group_about1" = "Up to 100,000 members";
|
"lng_group_about1" = "Up to 100,000 members";
|
||||||
|
|
|
@ -7,8 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "chat_helpers/emoji_list_widget.h"
|
#include "chat_helpers/emoji_list_widget.h"
|
||||||
|
|
||||||
|
#include "base/unixtime.h"
|
||||||
|
#include "ui/text/format_values.h"
|
||||||
#include "ui/effects/animations.h"
|
#include "ui/effects/animations.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "ui/widgets/shadow.h"
|
#include "ui/widgets/shadow.h"
|
||||||
#include "ui/text/custom_emoji_instance.h"
|
#include "ui/text/custom_emoji_instance.h"
|
||||||
#include "ui/effects/ripple_animation.h"
|
#include "ui/effects/ripple_animation.h"
|
||||||
|
@ -33,7 +36,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "settings/settings_premium.h"
|
#include "settings/settings_premium.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "facades.h"
|
|
||||||
#include "styles/style_chat_helpers.h"
|
#include "styles/style_chat_helpers.h"
|
||||||
|
|
||||||
namespace ChatHelpers {
|
namespace ChatHelpers {
|
||||||
|
@ -794,6 +796,45 @@ void EmojiListWidget::fillRecentFrom(const std::vector<DocumentId> &list) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmojiListWidget::fillContextMenu(
|
||||||
|
not_null<Ui::PopupMenu*> menu,
|
||||||
|
SendMenu::Type type) {
|
||||||
|
if (v::is_null(_selected)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto over = std::get_if<OverEmoji>(&_selected);
|
||||||
|
if (!over) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto section = over->section;
|
||||||
|
const auto index = over->index;
|
||||||
|
// Ignore the default status.
|
||||||
|
if (!index && (section == int(Section::Recent))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto chosen = lookupCustomEmoji(index, section);
|
||||||
|
if (!chosen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const auto &value : { 3600, 3600 * 8, 3600 * 24, 3600 * 24 * 7 }) {
|
||||||
|
const auto text = tr::lng_emoji_status_menu_duration_any(
|
||||||
|
tr::now,
|
||||||
|
lt_duration,
|
||||||
|
Ui::FormatMuteFor(value));
|
||||||
|
menu->addAction(text, crl::guard(this, [=] {
|
||||||
|
selectCustom(
|
||||||
|
chosen,
|
||||||
|
{ .scheduled = base::unixtime::now() + value } );
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
const auto options = Api::SendOptions{
|
||||||
|
.scheduled = TabbedSelector::kPickCustomTimeId,
|
||||||
|
};
|
||||||
|
menu->addAction(
|
||||||
|
tr::lng_manage_messages_ttl_after_custom(tr::now),
|
||||||
|
crl::guard(this, [=] { selectCustom(chosen, options); }));
|
||||||
|
}
|
||||||
|
|
||||||
void EmojiListWidget::paintEvent(QPaintEvent *e) {
|
void EmojiListWidget::paintEvent(QPaintEvent *e) {
|
||||||
Painter p(this);
|
Painter p(this);
|
||||||
|
|
||||||
|
@ -1050,6 +1091,27 @@ bool EmojiListWidget::checkPickerHide() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DocumentData *EmojiListWidget::lookupCustomEmoji(
|
||||||
|
int index,
|
||||||
|
int section) const {
|
||||||
|
if (section == int(Section::Recent)
|
||||||
|
&& index < _recent.size()) {
|
||||||
|
const auto document = std::get_if<RecentEmojiDocument>(
|
||||||
|
&_recent[index].id.data);
|
||||||
|
const auto custom = document
|
||||||
|
? session().data().document(document->id).get()
|
||||||
|
: nullptr;
|
||||||
|
if (custom && custom->sticker()) {
|
||||||
|
return custom;
|
||||||
|
}
|
||||||
|
} else if (section >= _staticCount
|
||||||
|
&& index < _custom[section - _staticCount].list.size()) {
|
||||||
|
auto &set = _custom[section - _staticCount];
|
||||||
|
return set.list[index].document;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
EmojiPtr EmojiListWidget::lookupOverEmoji(const OverEmoji *over) const {
|
EmojiPtr EmojiListWidget::lookupOverEmoji(const OverEmoji *over) const {
|
||||||
const auto section = over ? over->section : -1;
|
const auto section = over ? over->section : -1;
|
||||||
const auto index = over ? over->index : -1;
|
const auto index = over ? over->index : -1;
|
||||||
|
@ -1131,21 +1193,9 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
selectEmoji(emoji);
|
selectEmoji(emoji);
|
||||||
} else if (section == int(Section::Recent)
|
} else if (const auto custom = lookupCustomEmoji(index, section)) {
|
||||||
&& index < _recent.size()) {
|
|
||||||
const auto document = std::get_if<RecentEmojiDocument>(
|
|
||||||
&_recent[index].id.data);
|
|
||||||
const auto custom = document
|
|
||||||
? session().data().document(document->id).get()
|
|
||||||
: nullptr;
|
|
||||||
if (custom && custom->sticker()) {
|
|
||||||
selectCustom(custom);
|
selectCustom(custom);
|
||||||
}
|
}
|
||||||
} else if (section >= _staticCount
|
|
||||||
&& index < _custom[section - _staticCount].list.size()) {
|
|
||||||
auto &set = _custom[section - _staticCount];
|
|
||||||
selectCustom(set.list[index].document);
|
|
||||||
}
|
|
||||||
} else if (const auto set = std::get_if<OverSet>(&pressed)) {
|
} else if (const auto set = std::get_if<OverSet>(&pressed)) {
|
||||||
Assert(set->section >= _staticCount
|
Assert(set->section >= _staticCount
|
||||||
&& set->section < _staticCount + _custom.size());
|
&& set->section < _staticCount + _custom.size());
|
||||||
|
@ -1201,7 +1251,9 @@ void EmojiListWidget::selectEmoji(EmojiPtr emoji) {
|
||||||
_chosen.fire_copy(emoji);
|
_chosen.fire_copy(emoji);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmojiListWidget::selectCustom(not_null<DocumentData*> document) {
|
void EmojiListWidget::selectCustom(
|
||||||
|
not_null<DocumentData*> document,
|
||||||
|
Api::SendOptions options) {
|
||||||
if (document->isPremiumEmoji()
|
if (document->isPremiumEmoji()
|
||||||
&& !document->session().premium()
|
&& !document->session().premium()
|
||||||
&& !_allowWithoutPremium) {
|
&& !_allowWithoutPremium) {
|
||||||
|
@ -1215,7 +1267,7 @@ void EmojiListWidget::selectCustom(not_null<DocumentData*> document) {
|
||||||
document->session().isTestMode(),
|
document->session().isTestMode(),
|
||||||
} });
|
} });
|
||||||
}
|
}
|
||||||
_customChosen.fire({ .document = document });
|
_customChosen.fire({ .document = document, .options = options });
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmojiListWidget::showPicker() {
|
void EmojiListWidget::showPicker() {
|
||||||
|
|
|
@ -126,6 +126,10 @@ public:
|
||||||
float64 progress,
|
float64 progress,
|
||||||
RectPart origin);
|
RectPart origin);
|
||||||
|
|
||||||
|
void fillContextMenu(
|
||||||
|
not_null<Ui::PopupMenu*> menu,
|
||||||
|
SendMenu::Type type) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void visibleTopBottomUpdated(
|
void visibleTopBottomUpdated(
|
||||||
int visibleTop,
|
int visibleTop,
|
||||||
|
@ -247,8 +251,13 @@ private:
|
||||||
void setPressed(OverState newPressed);
|
void setPressed(OverState newPressed);
|
||||||
|
|
||||||
[[nodiscard]] EmojiPtr lookupOverEmoji(const OverEmoji *over) const;
|
[[nodiscard]] EmojiPtr lookupOverEmoji(const OverEmoji *over) const;
|
||||||
|
[[nodiscard]] DocumentData *lookupCustomEmoji(
|
||||||
|
int index,
|
||||||
|
int section) const;
|
||||||
void selectEmoji(EmojiPtr emoji);
|
void selectEmoji(EmojiPtr emoji);
|
||||||
void selectCustom(not_null<DocumentData*> document);
|
void selectCustom(
|
||||||
|
not_null<DocumentData*> document,
|
||||||
|
Api::SendOptions options = Api::SendOptions());
|
||||||
void paint(QPainter &p, ExpandingContext context, QRect clip);
|
void paint(QPainter &p, ExpandingContext context, QRect clip);
|
||||||
void drawCollapsedBadge(QPainter &p, QPoint position, int count);
|
void drawCollapsedBadge(QPainter &p, QPoint position, int count);
|
||||||
void drawRecent(
|
void drawRecent(
|
||||||
|
|
|
@ -60,6 +60,7 @@ class GifsListWidget;
|
||||||
|
|
||||||
class TabbedSelector : public Ui::RpWidget {
|
class TabbedSelector : public Ui::RpWidget {
|
||||||
public:
|
public:
|
||||||
|
static constexpr auto kPickCustomTimeId = -1;
|
||||||
struct FileChosen {
|
struct FileChosen {
|
||||||
not_null<DocumentData*> document;
|
not_null<DocumentData*> document;
|
||||||
Api::SendOptions options;
|
Api::SendOptions options;
|
||||||
|
|
|
@ -239,14 +239,18 @@ void EmojiStatuses::updateColored(const MTPDmessages_stickerSet &data) {
|
||||||
_coloredUpdated.fire({});
|
_coloredUpdated.fire({});
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmojiStatuses::set(DocumentId id) {
|
void EmojiStatuses::set(DocumentId id, TimeId until) {
|
||||||
auto &api = _owner->session().api();
|
auto &api = _owner->session().api();
|
||||||
if (_sentRequestId) {
|
if (_sentRequestId) {
|
||||||
api.request(base::take(_sentRequestId)).cancel();
|
api.request(base::take(_sentRequestId)).cancel();
|
||||||
}
|
}
|
||||||
_owner->session().user()->setEmojiStatus(id);
|
_owner->session().user()->setEmojiStatus(id, until);
|
||||||
_sentRequestId = api.request(MTPaccount_UpdateEmojiStatus(
|
_sentRequestId = api.request(MTPaccount_UpdateEmojiStatus(
|
||||||
id ? MTP_emojiStatus(MTP_long(id)) : MTP_emojiStatusEmpty()
|
!id
|
||||||
|
? MTP_emojiStatusEmpty()
|
||||||
|
: !until
|
||||||
|
? MTP_emojiStatus(MTP_long(id))
|
||||||
|
: MTP_emojiStatusUntil(MTP_long(id), MTP_int(until))
|
||||||
)).done([=] {
|
)).done([=] {
|
||||||
_sentRequestId = 0;
|
_sentRequestId = 0;
|
||||||
}).fail([=] {
|
}).fail([=] {
|
||||||
|
|
|
@ -48,7 +48,7 @@ public:
|
||||||
[[nodiscard]] rpl::producer<> defaultUpdates() const;
|
[[nodiscard]] rpl::producer<> defaultUpdates() const;
|
||||||
[[nodiscard]] rpl::producer<> coloredUpdates() const;
|
[[nodiscard]] rpl::producer<> coloredUpdates() const;
|
||||||
|
|
||||||
void set(DocumentId id);
|
void set(DocumentId id, TimeId until = 0);
|
||||||
[[nodiscard]] bool setting() const;
|
[[nodiscard]] bool setting() const;
|
||||||
|
|
||||||
void registerAutomaticClear(not_null<UserData*> user, TimeId until);
|
void registerAutomaticClear(not_null<UserData*> user, TimeId until);
|
||||||
|
|
|
@ -22,10 +22,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "info/info_controller.h"
|
#include "info/info_controller.h"
|
||||||
#include "info/info_memento.h"
|
#include "info/info_memento.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
#include "menu/menu_send.h" // SendMenu::Type.
|
||||||
|
#include "ui/boxes/confirm_box.h"
|
||||||
|
#include "ui/boxes/time_picker_box.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/effects/ripple_animation.h"
|
#include "ui/effects/ripple_animation.h"
|
||||||
#include "ui/text/text_block.h"
|
#include "ui/text/format_values.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/special_buttons.h"
|
#include "ui/special_buttons.h"
|
||||||
#include "ui/unread_badge.h"
|
#include "ui/unread_badge.h"
|
||||||
|
@ -80,6 +83,26 @@ auto ChatStatusText(int fullCount, int onlineCount, bool isGroup) {
|
||||||
: tr::lng_channel_status(tr::now);
|
: tr::lng_channel_status(tr::now);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void PickUntilBox(not_null<Ui::GenericBox*> box, Fn<void(TimeId)> callback) {
|
||||||
|
box->setTitle(tr::lng_emoji_status_for_title());
|
||||||
|
|
||||||
|
const auto seconds = Ui::DefaultTimePickerValues();
|
||||||
|
const auto phrases = ranges::views::all(
|
||||||
|
seconds
|
||||||
|
) | ranges::views::transform(Ui::FormatMuteFor) | ranges::to_vector;
|
||||||
|
|
||||||
|
const auto pickerCallback = Ui::TimePickerBox(box, seconds, phrases, 0);
|
||||||
|
|
||||||
|
Ui::ConfirmBox(box, {
|
||||||
|
.confirmed = [=] {
|
||||||
|
callback(pickerCallback());
|
||||||
|
box->closeBox();
|
||||||
|
},
|
||||||
|
.confirmText = tr::lng_emoji_status_for_submit(),
|
||||||
|
.cancelText = tr::lng_cancel(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
BadgeView::BadgeView(
|
BadgeView::BadgeView(
|
||||||
|
@ -301,17 +324,37 @@ void EmojiStatusPanel::create(
|
||||||
_panel->hide();
|
_panel->hide();
|
||||||
_panel->selector()->setAllowEmojiWithoutPremium(false);
|
_panel->selector()->setAllowEmojiWithoutPremium(false);
|
||||||
|
|
||||||
|
struct Chosen {
|
||||||
|
DocumentId id = 0;
|
||||||
|
TimeId until = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
_panel->selector()->contextMenuRequested(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
_panel->selector()->showMenuWithType(SendMenu::Type::Scheduled);
|
||||||
|
}, _panel->lifetime());
|
||||||
|
|
||||||
auto statusChosen = _panel->selector()->customEmojiChosen(
|
auto statusChosen = _panel->selector()->customEmojiChosen(
|
||||||
) | rpl::map([=](Selector::FileChosen data) {
|
) | rpl::map([=](Selector::FileChosen data) {
|
||||||
return data.document->id;
|
return Chosen{ data.document->id, data.options.scheduled };
|
||||||
});
|
});
|
||||||
|
|
||||||
rpl::merge(
|
rpl::merge(
|
||||||
std::move(statusChosen),
|
std::move(statusChosen),
|
||||||
_panel->selector()->emojiChosen() | rpl::map_to(DocumentId())
|
_panel->selector()->emojiChosen() | rpl::map_to(Chosen())
|
||||||
) | rpl::start_with_next([=](DocumentId id) {
|
) | rpl::start_with_next([=](const Chosen chosen) {
|
||||||
controller->session().data().emojiStatuses().set(id);
|
if (chosen.until == ChatHelpers::TabbedSelector::kPickCustomTimeId) {
|
||||||
|
controller->show(Box(PickUntilBox, [=](TimeId seconds) {
|
||||||
|
controller->session().data().emojiStatuses().set(
|
||||||
|
chosen.id,
|
||||||
|
base::unixtime::now() + seconds);
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
controller->session().data().emojiStatuses().set(
|
||||||
|
chosen.id,
|
||||||
|
chosen.until);
|
||||||
_panel->hideAnimated();
|
_panel->hideAnimated();
|
||||||
|
}
|
||||||
}, _panel->lifetime());
|
}, _panel->lifetime());
|
||||||
|
|
||||||
_panel->selector()->showPromoForPremiumEmoji();
|
_panel->selector()->showPromoForPremiumEmoji();
|
||||||
|
|
|
@ -171,24 +171,7 @@ void PickMuteBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) {
|
||||||
struct State {
|
struct State {
|
||||||
base::unique_qptr<Ui::PopupMenu> menu;
|
base::unique_qptr<Ui::PopupMenu> menu;
|
||||||
};
|
};
|
||||||
const auto seconds = std::vector<TimeId>{
|
const auto seconds = Ui::DefaultTimePickerValues();
|
||||||
(60 * 15),
|
|
||||||
(60 * 30),
|
|
||||||
(3600 * 1),
|
|
||||||
(3600 * 2),
|
|
||||||
(3600 * 3),
|
|
||||||
(3600 * 4),
|
|
||||||
(3600 * 8),
|
|
||||||
(3600 * 12),
|
|
||||||
(86400 * 1),
|
|
||||||
(86400 * 2),
|
|
||||||
(86400 * 3),
|
|
||||||
(86400 * 7 * 1),
|
|
||||||
(86400 * 7 * 2),
|
|
||||||
(86400 * 31 * 1),
|
|
||||||
(86400 * 31 * 2),
|
|
||||||
(86400 * 31 * 3),
|
|
||||||
};
|
|
||||||
const auto phrases = ranges::views::all(
|
const auto phrases = ranges::views::all(
|
||||||
seconds
|
seconds
|
||||||
) | ranges::views::transform(Ui::FormatMuteFor) | ranges::to_vector;
|
) | ranges::views::transform(Ui::FormatMuteFor) | ranges::to_vector;
|
||||||
|
|
|
@ -24,6 +24,27 @@ constexpr auto kMinYScale = 0.2;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
std::vector<TimeId> DefaultTimePickerValues() {
|
||||||
|
return {
|
||||||
|
(60 * 15),
|
||||||
|
(60 * 30),
|
||||||
|
(3600 * 1),
|
||||||
|
(3600 * 2),
|
||||||
|
(3600 * 3),
|
||||||
|
(3600 * 4),
|
||||||
|
(3600 * 8),
|
||||||
|
(3600 * 12),
|
||||||
|
(86400 * 1),
|
||||||
|
(86400 * 2),
|
||||||
|
(86400 * 3),
|
||||||
|
(86400 * 7 * 1),
|
||||||
|
(86400 * 7 * 2),
|
||||||
|
(86400 * 31 * 1),
|
||||||
|
(86400 * 31 * 2),
|
||||||
|
(86400 * 31 * 3),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
Fn<TimeId()> TimePickerBox(
|
Fn<TimeId()> TimePickerBox(
|
||||||
not_null<GenericBox*> box,
|
not_null<GenericBox*> box,
|
||||||
std::vector<TimeId> values,
|
std::vector<TimeId> values,
|
||||||
|
|
|
@ -11,7 +11,9 @@ namespace Ui {
|
||||||
|
|
||||||
class GenericBox;
|
class GenericBox;
|
||||||
|
|
||||||
Fn<TimeId()> TimePickerBox(
|
[[nodiscard]] std::vector<TimeId> DefaultTimePickerValues();
|
||||||
|
|
||||||
|
[[nodiscard]] Fn<TimeId()> TimePickerBox(
|
||||||
not_null<GenericBox*> box,
|
not_null<GenericBox*> box,
|
||||||
std::vector<TimeId> values,
|
std::vector<TimeId> values,
|
||||||
std::vector<QString> phrases,
|
std::vector<QString> phrases,
|
||||||
|
|
Loading…
Add table
Reference in a new issue