mirror of
https://github.com/kotatogram/kotatogram-desktop
synced 2025-08-31 14:45:14 +00:00
Allow editing tag names in Saved Messages.
This commit is contained in:
@@ -41,6 +41,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "menu/menu_send.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/boxes/show_or_premium_box.h"
|
||||
#include "ui/widgets/fields/input_field.h"
|
||||
#include "ui/power_saving.h"
|
||||
#include "boxes/delete_messages_box.h"
|
||||
#include "boxes/report_messages_box.h"
|
||||
#include "boxes/sticker_set_box.h"
|
||||
@@ -85,6 +87,7 @@ namespace HistoryView {
|
||||
namespace {
|
||||
|
||||
constexpr auto kRescheduleLimit = 20;
|
||||
constexpr auto kTagNameLimit = 12;
|
||||
|
||||
bool HasEditMessageAction(
|
||||
const ContextMenuRequest &request,
|
||||
@@ -980,6 +983,112 @@ void AddCopyLinkAction(
|
||||
&st::menuIconCopy);
|
||||
}
|
||||
|
||||
void EditTagBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> controller,
|
||||
const Data::ReactionId &id) {
|
||||
const auto owner = &controller->session().data();
|
||||
const auto title = owner->reactions().myTagTitle(id);
|
||||
box->setTitle(title.isEmpty()
|
||||
? tr::lng_context_tag_add_name()
|
||||
: tr::lng_context_tag_edit_name());
|
||||
box->addRow(object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_edit_tag_about(),
|
||||
st::editTagAbout));
|
||||
const auto field = box->addRow(object_ptr<Ui::InputField>(
|
||||
box,
|
||||
st::editTagField,
|
||||
tr::lng_edit_tag_name(),
|
||||
title));
|
||||
field->setMaxLength(kTagNameLimit * 2);
|
||||
box->setFocusCallback([=] {
|
||||
field->setFocusFast();
|
||||
});
|
||||
|
||||
struct State {
|
||||
std::unique_ptr<Ui::Text::CustomEmoji> custom;
|
||||
QImage image;
|
||||
rpl::variable<int> length;
|
||||
};
|
||||
const auto state = field->lifetime().make_state<State>();
|
||||
state->length = rpl::single(
|
||||
int(title.size())
|
||||
) | rpl::then(field->changes() | rpl::map([=] {
|
||||
return int(field->getLastText().size());
|
||||
}));
|
||||
|
||||
if (const auto customId = id.custom()) {
|
||||
state->custom = owner->customEmojiManager().create(
|
||||
customId,
|
||||
[=] { field->update(); });
|
||||
} else {
|
||||
owner->reactions().preloadImageFor(id);
|
||||
}
|
||||
field->paintRequest() | rpl::start_with_next([=](QRect clip) {
|
||||
auto p = QPainter(field);
|
||||
const auto top = st::editTagField.textMargins.top();
|
||||
if (const auto custom = state->custom.get()) {
|
||||
const auto inactive = !field->window()->isActiveWindow();
|
||||
custom->paint(p, {
|
||||
.textColor = st::windowFg->c,
|
||||
.now = crl::now(),
|
||||
.position = QPoint(0, top),
|
||||
.paused = inactive || On(PowerSaving::kEmojiChat),
|
||||
});
|
||||
} else {
|
||||
if (state->image.isNull()) {
|
||||
state->image = owner->reactions().resolveImageFor(
|
||||
id,
|
||||
::Data::Reactions::ImageSize::InlineList);
|
||||
}
|
||||
if (!state->image.isNull()) {
|
||||
const auto size = st::reactionInlineSize;
|
||||
const auto skip = (size - st::reactionInlineImage) / 2;
|
||||
p.drawImage(skip, top + skip, state->image);
|
||||
}
|
||||
}
|
||||
}, field->lifetime());
|
||||
const auto warning = Ui::CreateChild<Ui::FlatLabel>(
|
||||
field,
|
||||
state->length.value() | rpl::map([](int count) {
|
||||
return (count > kTagNameLimit / 2)
|
||||
? QString::number(kTagNameLimit - count)
|
||||
: QString();
|
||||
}),
|
||||
st::editTagLimit);
|
||||
state->length.value() | rpl::map(
|
||||
rpl::mappers::_1 > kTagNameLimit
|
||||
) | rpl::start_with_next([=](bool exceeded) {
|
||||
warning->setTextColorOverride(exceeded
|
||||
? st::attentionButtonFg->c
|
||||
: std::optional<QColor>());
|
||||
}, warning->lifetime());
|
||||
rpl::combine(
|
||||
field->sizeValue(),
|
||||
warning->sizeValue()
|
||||
) | rpl::start_with_next([=] {
|
||||
warning->moveToRight(0, st::editTagField.textMargins.top());
|
||||
}, warning->lifetime());
|
||||
warning->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
box->addButton(tr::lng_settings_save(), [=] {
|
||||
const auto text = field->getLastText();
|
||||
if (text.size() > kTagNameLimit) {
|
||||
field->showError();
|
||||
return;
|
||||
}
|
||||
const auto weak = Ui::MakeWeak(box);
|
||||
controller->session().data().reactions().renameTag(id, text);
|
||||
if (const auto strong = weak.data()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
});
|
||||
box->addButton(tr::lng_cancel(), [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ContextMenuRequest::ContextMenuRequest(
|
||||
@@ -1342,6 +1451,13 @@ void ShowTagMenu(
|
||||
});
|
||||
}, &st::menuIconFave);
|
||||
|
||||
const auto editLabel = owner->reactions().myTagTitle(id).isEmpty()
|
||||
? tr::lng_context_tag_add_name(tr::now)
|
||||
: tr::lng_context_tag_edit_name(tr::now);
|
||||
(*menu)->addAction(editLabel, [=] {
|
||||
controller->show(Box(EditTagBox, controller, id));
|
||||
}, &st::menuIconEdit);
|
||||
|
||||
const auto removeTag = [=] {
|
||||
if (const auto item = owner->message(itemId)) {
|
||||
const auto &list = item->reactions();
|
||||
|
@@ -435,6 +435,21 @@ Message::~Message() {
|
||||
_fromNameStatus = nullptr;
|
||||
checkHeavyPart();
|
||||
}
|
||||
setReactions(nullptr);
|
||||
}
|
||||
|
||||
void Message::setReactions(std::unique_ptr<Reactions::InlineList> list) {
|
||||
auto was = _reactions
|
||||
? _reactions->computeTagsList()
|
||||
: std::vector<Data::ReactionId>();
|
||||
_reactions = std::move(list);
|
||||
auto now = _reactions
|
||||
? _reactions->computeTagsList()
|
||||
: std::vector<Data::ReactionId>();
|
||||
if (!was.empty() || !now.empty()) {
|
||||
auto &owner = history()->owner();
|
||||
owner.viewTagsChanged(this, std::move(was), std::move(now));
|
||||
}
|
||||
}
|
||||
|
||||
void Message::refreshRightBadge() {
|
||||
@@ -2958,7 +2973,7 @@ void Message::refreshReactions() {
|
||||
const auto item = data();
|
||||
const auto &list = item->reactions();
|
||||
if (list.empty() || embedReactionsInBottomInfo()) {
|
||||
_reactions = nullptr;
|
||||
setReactions(nullptr);
|
||||
return;
|
||||
}
|
||||
using namespace Reactions;
|
||||
@@ -2988,13 +3003,19 @@ void Message::refreshReactions() {
|
||||
}
|
||||
});
|
||||
};
|
||||
_reactions = std::make_unique<InlineList>(
|
||||
setReactions(std::make_unique<InlineList>(
|
||||
&item->history()->owner().reactions(),
|
||||
handlerFactory,
|
||||
[=] { customEmojiRepaint(); },
|
||||
std::move(reactionsData));
|
||||
std::move(reactionsData)));
|
||||
} else {
|
||||
auto was = _reactions->computeTagsList();
|
||||
_reactions->update(std::move(reactionsData), width());
|
||||
auto now = _reactions->computeTagsList();
|
||||
if (!was.empty() || !now.empty()) {
|
||||
auto &owner = history()->owner();
|
||||
owner.viewTagsChanged(this, std::move(was), std::move(now));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -289,6 +289,7 @@ private:
|
||||
[[nodiscard]] ClickHandlerPtr psaTooltipLink() const;
|
||||
void psaTooltipToggled(bool shown) const;
|
||||
|
||||
void setReactions(std::unique_ptr<Reactions::InlineList> list);
|
||||
void refreshRightBadge();
|
||||
void refreshReactions();
|
||||
void validateFromNameText(PeerData *from) const;
|
||||
|
@@ -88,6 +88,19 @@ void InlineList::removeSkipBlock() {
|
||||
_skipBlock = {};
|
||||
}
|
||||
|
||||
bool InlineList::areTags() const {
|
||||
return _data.flags & Data::Flag::Tags;
|
||||
}
|
||||
|
||||
std::vector<ReactionId> InlineList::computeTagsList() const {
|
||||
if (!areTags()) {
|
||||
return {};
|
||||
}
|
||||
return _buttons | ranges::views::transform(
|
||||
&Button::id
|
||||
) | ranges::to_vector;
|
||||
}
|
||||
|
||||
bool InlineList::hasCustomEmoji() const {
|
||||
return _hasCustomEmoji;
|
||||
}
|
||||
@@ -119,7 +132,7 @@ void InlineList::layoutButtons() {
|
||||
) | ranges::views::transform([](const MessageReaction &reaction) {
|
||||
return not_null{ &reaction };
|
||||
}) | ranges::to_vector;
|
||||
const auto tags = _data.flags & Data::Flag::Tags;
|
||||
const auto tags = areTags();
|
||||
const auto &infos = _owner->myTagsInfo();
|
||||
if (!tags) {
|
||||
const auto &list = _owner->list(::Data::Reactions::Type::All);
|
||||
@@ -148,10 +161,7 @@ void InlineList::layoutButtons() {
|
||||
? std::move(*i)
|
||||
: prepareButtonWithId(id));
|
||||
if (tags) {
|
||||
const auto i = ranges::find(infos, id, &::Data::MyTagInfo::id);
|
||||
setButtonTag(
|
||||
buttons.back(),
|
||||
(i == end(infos)) ? QString() : i->title);
|
||||
setButtonTag(buttons.back(), _owner->myTagTitle(id));
|
||||
} else if (const auto j = _data.recent.find(id)
|
||||
; j != end(_data.recent) && !j->second.empty()) {
|
||||
setButtonUserpics(buttons.back(), j->second);
|
||||
@@ -367,7 +377,7 @@ void InlineList::paint(
|
||||
const auto padding = st::reactionInlinePadding;
|
||||
const auto size = st::reactionInlineSize;
|
||||
const auto skip = (size - st::reactionInlineImage) / 2;
|
||||
const auto tags = (_data.flags & Data::Flag::Tags);
|
||||
const auto tags = areTags();
|
||||
const auto inbubble = (_data.flags & Data::Flag::InBubble);
|
||||
const auto flipped = (_data.flags & Data::Flag::Flipped);
|
||||
p.setFont(tags ? st::reactionInlineTagFont : st::semiboldFont);
|
||||
@@ -613,7 +623,7 @@ void InlineList::paintSingleBg(
|
||||
const QColor &color,
|
||||
float64 opacity) const {
|
||||
p.setOpacity(opacity);
|
||||
if (!(_data.flags & Data::Flag::Tags)) {
|
||||
if (!areTags()) {
|
||||
const auto radius = fill.height() / 2.;
|
||||
p.setBrush(color);
|
||||
p.drawRoundedRect(fill, radius, radius);
|
||||
|
@@ -70,6 +70,8 @@ public:
|
||||
void updateSkipBlock(int width, int height);
|
||||
void removeSkipBlock();
|
||||
|
||||
[[nodiscard]] bool areTags() const;
|
||||
[[nodiscard]] std::vector<ReactionId> computeTagsList() const;
|
||||
[[nodiscard]] bool hasCustomEmoji() const;
|
||||
void unloadCustomEmoji();
|
||||
|
||||
|
Reference in New Issue
Block a user