mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
Added ability to tag with emoji recent forwarded messages to self.
This commit is contained in:
@@ -918,6 +918,8 @@ PRIVATE
|
|||||||
history/view/history_view_schedule_box.h
|
history/view/history_view_schedule_box.h
|
||||||
history/view/history_view_scheduled_section.cpp
|
history/view/history_view_scheduled_section.cpp
|
||||||
history/view/history_view_scheduled_section.h
|
history/view/history_view_scheduled_section.h
|
||||||
|
history/view/history_view_self_forwards_tagger.cpp
|
||||||
|
history/view/history_view_self_forwards_tagger.h
|
||||||
history/view/history_view_send_action.cpp
|
history/view/history_view_send_action.cpp
|
||||||
history/view/history_view_send_action.h
|
history/view/history_view_send_action.h
|
||||||
history/view/history_view_service_message.cpp
|
history/view/history_view_service_message.cpp
|
||||||
|
|||||||
Binary file not shown.
@@ -43,6 +43,7 @@
|
|||||||
<file alias="cake.tgs">../../animations/cake.tgs</file>
|
<file alias="cake.tgs">../../animations/cake.tgs</file>
|
||||||
<file alias="camera_outline.tgs">../../animations/camera_outline.tgs</file>
|
<file alias="camera_outline.tgs">../../animations/camera_outline.tgs</file>
|
||||||
<file alias="photo_suggest_icon.tgs">../../animations/photo_suggest_icon.tgs</file>
|
<file alias="photo_suggest_icon.tgs">../../animations/photo_suggest_icon.tgs</file>
|
||||||
|
<file alias="toast/saved_messages.tgs">../../animations/toast/saved_messages.tgs</file>
|
||||||
|
|
||||||
<file alias="dice_idle.tgs">../../animations/dice/dice_idle.tgs</file>
|
<file alias="dice_idle.tgs">../../animations/dice/dice_idle.tgs</file>
|
||||||
<file alias="dart_idle.tgs">../../animations/dice/dart_idle.tgs</file>
|
<file alias="dart_idle.tgs">../../animations/dice/dart_idle.tgs</file>
|
||||||
|
|||||||
@@ -815,6 +815,15 @@ reactPanelScrollRounded: ScrollArea(emojiScroll) {
|
|||||||
deltat: 14px;
|
deltat: 14px;
|
||||||
deltab: 14px;
|
deltab: 14px;
|
||||||
}
|
}
|
||||||
|
selfForwardsTaggerStripSkip: 46px;
|
||||||
|
selfForwardsTaggerIcon: size(32px, 32px);
|
||||||
|
selfForwardsTaggerToast: Toast(defaultToast) {
|
||||||
|
minWidth: 160px;
|
||||||
|
maxWidth: 380px;
|
||||||
|
radius: 9px;
|
||||||
|
padding: margins(54px, 12px, 19px, 12px);
|
||||||
|
iconPosition: point(15px, 6px);
|
||||||
|
}
|
||||||
|
|
||||||
choosePeerGroupIcon: icon {{ "info/edit/create_group", lightButtonFg }};
|
choosePeerGroupIcon: icon {{ "info/edit/create_group", lightButtonFg }};
|
||||||
choosePeerChannelIcon: icon {{ "info/edit/create_channel", lightButtonFg }};
|
choosePeerChannelIcon: icon {{ "info/edit/create_channel", lightButtonFg }};
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||||||
#include "history/view/history_view_item_preview.h"
|
#include "history/view/history_view_item_preview.h"
|
||||||
#include "history/view/history_view_reply.h"
|
#include "history/view/history_view_reply.h"
|
||||||
#include "history/view/history_view_requests_bar.h"
|
#include "history/view/history_view_requests_bar.h"
|
||||||
|
#include "history/view/history_view_self_forwards_tagger.h"
|
||||||
#include "history/view/history_view_sticker_toast.h"
|
#include "history/view/history_view_sticker_toast.h"
|
||||||
#include "history/view/history_view_subsection_tabs.h"
|
#include "history/view/history_view_subsection_tabs.h"
|
||||||
#include "history/view/history_view_translate_bar.h"
|
#include "history/view/history_view_translate_bar.h"
|
||||||
@@ -1018,6 +1019,12 @@ HistoryWidget::HistoryWidget(
|
|||||||
}
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
|
_selfForwardsTagger = std::make_unique<HistoryView::SelfForwardsTagger>(
|
||||||
|
controller,
|
||||||
|
this,
|
||||||
|
[=] { return _list; },
|
||||||
|
_scroll.data());
|
||||||
|
|
||||||
if (session().supportMode()) {
|
if (session().supportMode()) {
|
||||||
session().data().chatListEntryRefreshes(
|
session().data().chatListEntryRefreshes(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ struct FileChosen;
|
|||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
class StickerToast;
|
class StickerToast;
|
||||||
class PaidReactionToast;
|
class PaidReactionToast;
|
||||||
|
class SelfForwardsTagger;
|
||||||
class TopBarWidget;
|
class TopBarWidget;
|
||||||
class PaysStatus;
|
class PaysStatus;
|
||||||
class ContactStatus;
|
class ContactStatus;
|
||||||
@@ -894,6 +895,7 @@ private:
|
|||||||
|
|
||||||
HistoryView::InfoTooltip _topToast;
|
HistoryView::InfoTooltip _topToast;
|
||||||
std::unique_ptr<HistoryView::StickerToast> _stickerToast;
|
std::unique_ptr<HistoryView::StickerToast> _stickerToast;
|
||||||
|
std::unique_ptr<HistoryView::SelfForwardsTagger> _selfForwardsTagger;
|
||||||
std::unique_ptr<ChooseMessagesForReport> _chooseForReport;
|
std::unique_ptr<ChooseMessagesForReport> _chooseForReport;
|
||||||
|
|
||||||
std::unique_ptr<HistoryView::PaidReactionToast> _paidReactionToast;
|
std::unique_ptr<HistoryView::PaidReactionToast> _paidReactionToast;
|
||||||
|
|||||||
@@ -0,0 +1,266 @@
|
|||||||
|
/*
|
||||||
|
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 "history/view/history_view_self_forwards_tagger.h"
|
||||||
|
|
||||||
|
#include "base/call_delayed.h"
|
||||||
|
#include "base/event_filter.h"
|
||||||
|
#include "base/timer_rpl.h"
|
||||||
|
#include "chat_helpers/share_message_phrase_factory.h"
|
||||||
|
#include "core/ui_integration.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_user.h"
|
||||||
|
#include "history/history_item.h"
|
||||||
|
#include "history/view/reactions/history_view_reactions_selector.h"
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
|
#include "lottie/lottie_icon.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
#include "ui/rect.h"
|
||||||
|
#include "ui/effects/show_animation.h"
|
||||||
|
#include "ui/text/text_utilities.h"
|
||||||
|
#include "ui/toast/toast_widget.h"
|
||||||
|
#include "ui/toast/toast.h"
|
||||||
|
#include "ui/widgets/tooltip.h"
|
||||||
|
#include "window/window_session_controller.h"
|
||||||
|
#include "styles/style_chat.h"
|
||||||
|
#include "styles/style_chat_helpers.h"
|
||||||
|
|
||||||
|
namespace HistoryView {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kInitTimer = crl::time(3000);
|
||||||
|
constexpr auto kTimerOnLeave = crl::time(2000);
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
SelfForwardsTagger::SelfForwardsTagger(
|
||||||
|
not_null<Window::SessionController*> controller,
|
||||||
|
not_null<Ui::RpWidget*> parent,
|
||||||
|
Fn<Ui::RpWidget*()> listWidget,
|
||||||
|
not_null<QWidget*> scroll)
|
||||||
|
: _controller(controller)
|
||||||
|
, _parent(parent)
|
||||||
|
, _listWidget(std::move(listWidget))
|
||||||
|
, _scroll(scroll) {
|
||||||
|
setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
SelfForwardsTagger::~SelfForwardsTagger() = default;
|
||||||
|
|
||||||
|
void SelfForwardsTagger::setup() {
|
||||||
|
_controller->session().data().recentSelfForwards(
|
||||||
|
) | rpl::start_with_next([=](const MessageIdsList &ids) {
|
||||||
|
showSelectorForMessages(ids);
|
||||||
|
}, _lifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SelfForwardsTagger::showSelectorForMessages(
|
||||||
|
const MessageIdsList &ids) {
|
||||||
|
if (ids.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto lastId = ids.back();
|
||||||
|
const auto item = _controller->session().data().message(lastId);
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
using namespace Reactions;
|
||||||
|
const auto reactions = Data::LookupPossibleReactions(item, true);
|
||||||
|
if (reactions.recent.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showToast(
|
||||||
|
rpl::variable<TextWithEntities>(
|
||||||
|
ChatHelpers::ForwardedMessagePhrase({
|
||||||
|
.toCount = 1,
|
||||||
|
.singleMessage = (ids.size() == 1),
|
||||||
|
.to1 = _controller->session().user(),
|
||||||
|
.toSelfWithPremiumIsEmpty = false,
|
||||||
|
})).current(),
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
const auto toastWidget = [&]() -> Ui::RpWidget* {
|
||||||
|
if (const auto toast = _toast.get()) {
|
||||||
|
return toast->widget();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}();
|
||||||
|
if (!toastWidget) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto toastWidth = toastWidget->width();
|
||||||
|
const auto selector = Ui::CreateChild<Selector>(
|
||||||
|
toastWidget->parentWidget(),
|
||||||
|
st::reactPanelEmojiPan,
|
||||||
|
_controller->uiShow(),
|
||||||
|
reactions,
|
||||||
|
TextWithEntities(),
|
||||||
|
[](bool) {},
|
||||||
|
IconFactory(),
|
||||||
|
[] { return false; },
|
||||||
|
false);
|
||||||
|
selector->setBubbleUp(true);
|
||||||
|
|
||||||
|
const auto hideAndDestroy = [
|
||||||
|
selectorWeak = base::make_weak(selector),
|
||||||
|
toastWidgetWeak = _toast] {
|
||||||
|
const auto selector = selectorWeak.get();
|
||||||
|
const auto toastWidget = toastWidgetWeak.get();
|
||||||
|
if (!selector || !toastWidget) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Ui::Animations::HideWidgets({ toastWidget->widget(), selector });
|
||||||
|
selector->shownValue(
|
||||||
|
) | rpl::start_with_next([toastWidgetWeak](bool shown) {
|
||||||
|
if (!shown) {
|
||||||
|
if (const auto toast = toastWidgetWeak.get()) {
|
||||||
|
delete toast->widget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, selector->lifetime());
|
||||||
|
};
|
||||||
|
|
||||||
|
selector->chosen(
|
||||||
|
) | rpl::start_with_next([=](ChosenReaction reaction) {
|
||||||
|
for (const auto &id : ids) {
|
||||||
|
if (const auto item = _controller->session().data().message(
|
||||||
|
id)) {
|
||||||
|
item->toggleReaction(
|
||||||
|
reaction.id,
|
||||||
|
HistoryReactionSource::Selector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hideAndDestroy();
|
||||||
|
}, selector->lifetime());
|
||||||
|
|
||||||
|
const auto eventFilterCallback = [=](not_null<QEvent*> event) {
|
||||||
|
if (event->type() == QEvent::MouseButtonPress) {
|
||||||
|
hideAndDestroy();
|
||||||
|
return base::EventFilterResult::Cancel;
|
||||||
|
}
|
||||||
|
return base::EventFilterResult::Continue;
|
||||||
|
};
|
||||||
|
base::install_event_filter(selector, _parent, eventFilterCallback);
|
||||||
|
if (const auto list = _listWidget()) {
|
||||||
|
list->lifetime().add([=] {
|
||||||
|
hideAndDestroy();
|
||||||
|
});
|
||||||
|
base::install_event_filter(selector, list, eventFilterCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
rpl::lifetime timerLifetime;
|
||||||
|
bool expanded = false;
|
||||||
|
};
|
||||||
|
const auto state = selector->lifetime().make_state<State>();
|
||||||
|
const auto restartTimer = [=](crl::time ms) {
|
||||||
|
state->timerLifetime.destroy();
|
||||||
|
base::timer_once(ms) | rpl::start_with_next([=] {
|
||||||
|
hideAndDestroy();
|
||||||
|
}, state->timerLifetime);
|
||||||
|
};
|
||||||
|
|
||||||
|
selector->willExpand() | rpl::start_with_next([=] {
|
||||||
|
state->expanded = true;
|
||||||
|
}, selector->lifetime());
|
||||||
|
|
||||||
|
base::install_event_filter(selector, [=](not_null<QEvent*> event) {
|
||||||
|
if (event->type() == QEvent::MouseButtonPress) {
|
||||||
|
state->timerLifetime.destroy();
|
||||||
|
return base::EventFilterResult::Continue;
|
||||||
|
} else if (!state->expanded && event->type() == QEvent::Enter) {
|
||||||
|
state->timerLifetime.destroy();
|
||||||
|
return base::EventFilterResult::Continue;
|
||||||
|
} else if (!state->expanded && event->type() == QEvent::Leave) {
|
||||||
|
restartTimer(kTimerOnLeave);
|
||||||
|
return base::EventFilterResult::Continue;
|
||||||
|
}
|
||||||
|
return base::EventFilterResult::Continue;
|
||||||
|
}, selector->lifetime());
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
_toast->widget(),
|
||||||
|
&QObject::destroyed,
|
||||||
|
selector,
|
||||||
|
[=] { delete selector; });
|
||||||
|
|
||||||
|
const auto selectorWidth = toastWidth;
|
||||||
|
selector->countWidth(selectorWidth, selectorWidth);
|
||||||
|
selector->initGeometry(_parent->height() / 2);
|
||||||
|
|
||||||
|
_toast->widget()->geometryValue(
|
||||||
|
) | rpl::start_with_next([=](const QRect &rect) {
|
||||||
|
if (rect.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selector->moveToLeft(
|
||||||
|
rect.x() + (rect.width() - selector->width()) / 2,
|
||||||
|
rect::bottom(rect) - st::selfForwardsTaggerStripSkip);
|
||||||
|
}, selector->lifetime());
|
||||||
|
restartTimer(kInitTimer);
|
||||||
|
selector->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SelfForwardsTagger::showToast(
|
||||||
|
const TextWithEntities &text,
|
||||||
|
Fn<void()> callback) {
|
||||||
|
hideToast();
|
||||||
|
_toast = Ui::Toast::Show(_scroll, Ui::Toast::Config{
|
||||||
|
.text = text,
|
||||||
|
.textContext = Core::TextContext({
|
||||||
|
.session = &_controller->session(),
|
||||||
|
}),
|
||||||
|
.st = &st::selfForwardsTaggerToast,
|
||||||
|
.attach = RectPart::Top,
|
||||||
|
.infinite = true,
|
||||||
|
});
|
||||||
|
if (const auto strong = _toast.get()) {
|
||||||
|
const auto widget = strong->widget();
|
||||||
|
createLottieIcon(widget);
|
||||||
|
if (callback) {
|
||||||
|
QObject::connect(widget, &QObject::destroyed, callback);
|
||||||
|
}
|
||||||
|
} else if (callback) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SelfForwardsTagger::createLottieIcon(
|
||||||
|
not_null<QWidget*> widget) {
|
||||||
|
const auto lottieWidget = Ui::CreateChild<Ui::RpWidget>(widget);
|
||||||
|
struct State {
|
||||||
|
std::unique_ptr<Lottie::Icon> lottieIcon;
|
||||||
|
};
|
||||||
|
const auto state = lottieWidget->lifetime().make_state<State>();
|
||||||
|
state->lottieIcon = Lottie::MakeIcon({
|
||||||
|
.name = u"toast/saved_messages"_q,
|
||||||
|
.sizeOverride = st::selfForwardsTaggerIcon,
|
||||||
|
});
|
||||||
|
const auto icon = state->lottieIcon.get();
|
||||||
|
lottieWidget->resize(st::selfForwardsTaggerIcon);
|
||||||
|
lottieWidget->move(st::selfForwardsTaggerToast.iconPosition);
|
||||||
|
lottieWidget->show();
|
||||||
|
lottieWidget->raise();
|
||||||
|
icon->animate(
|
||||||
|
[=] { lottieWidget->update(); },
|
||||||
|
0,
|
||||||
|
icon->framesCount() - 1);
|
||||||
|
lottieWidget->paintRequest() | rpl::start_with_next([=] {
|
||||||
|
auto p = QPainter(lottieWidget);
|
||||||
|
icon->paint(p, 0, 0);
|
||||||
|
}, lottieWidget->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SelfForwardsTagger::hideToast() {
|
||||||
|
if (const auto strong = _toast.get()) {
|
||||||
|
strong->hideAnimated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace HistoryView
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
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 "base/weak_ptr.h"
|
||||||
|
|
||||||
|
namespace Lottie {
|
||||||
|
class Icon;
|
||||||
|
} // namespace Lottie
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
class Session;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
|
namespace Main {
|
||||||
|
class Session;
|
||||||
|
} // namespace Main
|
||||||
|
|
||||||
|
namespace Window {
|
||||||
|
class SessionController;
|
||||||
|
} // namespace Window
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class RpWidget;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Ui::Toast {
|
||||||
|
class Instance;
|
||||||
|
} // namespace Ui::Toast
|
||||||
|
|
||||||
|
namespace HistoryView {
|
||||||
|
|
||||||
|
class SelfForwardsTagger final : public base::has_weak_ptr {
|
||||||
|
public:
|
||||||
|
SelfForwardsTagger(
|
||||||
|
not_null<Window::SessionController*> controller,
|
||||||
|
not_null<Ui::RpWidget*> parent,
|
||||||
|
Fn<Ui::RpWidget*()> listWidget,
|
||||||
|
not_null<QWidget*> scroll);
|
||||||
|
|
||||||
|
~SelfForwardsTagger();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setup();
|
||||||
|
void showSelectorForMessages(const MessageIdsList &ids);
|
||||||
|
void showToast(const TextWithEntities &text, Fn<void()> callback);
|
||||||
|
void createLottieIcon(not_null<QWidget*> widget);
|
||||||
|
void hideToast();
|
||||||
|
[[nodiscard]] QRect toastGeometry() const;
|
||||||
|
|
||||||
|
const not_null<Window::SessionController*> _controller;
|
||||||
|
const not_null<Ui::RpWidget*> _parent;
|
||||||
|
const Fn<Ui::RpWidget*()> _listWidget;
|
||||||
|
const not_null<QWidget*> _scroll;
|
||||||
|
|
||||||
|
base::weak_ptr<Ui::Toast::Instance> _toast;
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace HistoryView
|
||||||
Reference in New Issue
Block a user