mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
Added ability to reply with timecode of currently playing voice message.
This commit is contained in:
@@ -5003,6 +5003,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_to_msg" = "Go To Message";
|
||||
"lng_context_reply_msg" = "Reply";
|
||||
"lng_context_quote_and_reply" = "Quote & Reply";
|
||||
"lng_context_reply_with_timecode" = "Reply with timecode";
|
||||
"lng_context_reply_to_task" = "Reply to Task";
|
||||
"lng_context_reply_to_poll_option" = "Reply to Option";
|
||||
"lng_context_copy_poll_option" = "Copy Option";
|
||||
|
||||
@@ -88,6 +88,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "menu/menu_item_download_files.h"
|
||||
#include "menu/menu_item_rate_transcribe.h"
|
||||
#include "menu/menu_item_rate_transcribe_session.h"
|
||||
#include "menu/menu_timecode_action.h"
|
||||
#include "menu/menu_sponsored.h"
|
||||
#include "core/application.h"
|
||||
#include "apiwrap.h"
|
||||
@@ -2963,6 +2964,24 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
||||
_widget->clearSelected();
|
||||
}
|
||||
}, &st::menuIconReply);
|
||||
const auto media = item->media();
|
||||
const auto document = media
|
||||
? media->document()
|
||||
: nullptr;
|
||||
if (canSendReply && document && document->isVoiceMessage()) {
|
||||
const auto msgId = item->fullId();
|
||||
if (const auto t = HistoryView::CurrentVoiceTimecode(msgId)) {
|
||||
Menu::AddTimecodeAction(
|
||||
_menu.get(),
|
||||
*t,
|
||||
HistoryView::VoiceTimecodeUpdates(msgId),
|
||||
[=] {
|
||||
const auto cur
|
||||
= HistoryView::CurrentVoiceTimecode(msgId);
|
||||
_widget->insertTextAtCursor(cur.value_or(*t));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -142,6 +142,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "chat_helpers/bot_keyboard.h"
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "menu/menu_send.h"
|
||||
#include "menu/menu_timecode_action.h"
|
||||
#include "mtproto/mtproto_config.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "settings/business/settings_quick_replies.h"
|
||||
@@ -5677,6 +5678,13 @@ bool HistoryWidget::insertBotCommand(const QString &cmd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void HistoryWidget::insertTextAtCursor(const QString &text) {
|
||||
if (!_canSendTexts) {
|
||||
return;
|
||||
}
|
||||
Menu::InsertTextAtCursor(_field, text);
|
||||
}
|
||||
|
||||
bool HistoryWidget::eventFilter(QObject *obj, QEvent *e) {
|
||||
if (e->type() == QEvent::KeyPress) {
|
||||
const auto k = static_cast<QKeyEvent*>(e);
|
||||
|
||||
@@ -242,6 +242,7 @@ public:
|
||||
void sendBotCommand(const Bot::SendCommandRequest &request);
|
||||
void hideSingleUseKeyboard(FullMsgId replyToId);
|
||||
bool insertBotCommand(const QString &cmd);
|
||||
void insertTextAtCursor(const QString &text);
|
||||
|
||||
bool eventFilter(QObject *obj, QEvent *e) override;
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/mime_type.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_session_settings.h"
|
||||
#include "menu/menu_timecode_action.h"
|
||||
#include "data/components/scheduled_messages.h"
|
||||
#include "data/data_histories.h"
|
||||
#include "data/data_saved_messages.h"
|
||||
@@ -395,6 +396,12 @@ ChatWidget::ChatWidget(
|
||||
}
|
||||
}, _inner->lifetime());
|
||||
|
||||
_inner->setInsertTextCallback([=](const QString &text) {
|
||||
if (const auto field = _composeControls->fieldForMention()) {
|
||||
Menu::InsertTextAtCursor(field, text);
|
||||
}
|
||||
});
|
||||
|
||||
_composeControls->sendActionUpdates(
|
||||
) | rpl::on_next([=](ComposeControls::SendActionUpdate &&data) {
|
||||
if (!_repliesRootId) {
|
||||
|
||||
@@ -52,6 +52,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "menu/menu_item_download_files.h"
|
||||
#include "menu/menu_item_rate_transcribe.h"
|
||||
#include "menu/menu_item_rate_transcribe_session.h"
|
||||
#include "menu/menu_timecode_action.h"
|
||||
#include "menu/menu_send.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/boxes/show_or_premium_box.h"
|
||||
@@ -94,6 +95,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_session_settings.h"
|
||||
#include "media/audio/media_audio.h"
|
||||
#include "media/player/media_player_instance.h"
|
||||
#include "spellcheck/spellcheck_types.h"
|
||||
#include "apiwrap.h"
|
||||
#include "styles/style_chat.h"
|
||||
@@ -1275,6 +1278,30 @@ void ShowWhoReadInfo(
|
||||
|
||||
} // namespace
|
||||
|
||||
std::optional<QString> CurrentVoiceTimecode(FullMsgId itemId) {
|
||||
const auto state = ::Media::Player::instance()->getState(
|
||||
AudioMsgId::Type::Voice);
|
||||
if (state.id.contextId() == itemId
|
||||
&& !::Media::Player::IsStoppedOrStopping(state.state)
|
||||
&& state.frequency > 0) {
|
||||
return Ui::FormatDurationText(state.position / state.frequency);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
rpl::producer<QString> VoiceTimecodeUpdates(FullMsgId itemId) {
|
||||
return ::Media::Player::instance()->updatedNotifier(
|
||||
) | rpl::filter([=](const ::Media::Player::TrackState &state) {
|
||||
return (state.id.type() == AudioMsgId::Type::Voice)
|
||||
&& (state.id.contextId() == itemId);
|
||||
}) | rpl::filter([](const ::Media::Player::TrackState &state) {
|
||||
return !::Media::Player::IsStoppedOrStopping(state.state)
|
||||
&& state.frequency > 0;
|
||||
}) | rpl::map([](const ::Media::Player::TrackState &state) {
|
||||
return Ui::FormatDurationText(state.position / state.frequency);
|
||||
}) | rpl::distinct_until_changed();
|
||||
}
|
||||
|
||||
void InsertPollHiddenResultsLabel(not_null<Ui::PopupMenu*> menu) {
|
||||
auto label = base::make_unique_q<Ui::Menu::MultilineAction>(
|
||||
menu->menu(),
|
||||
@@ -1324,6 +1351,32 @@ void FillContextMenuItems(
|
||||
&& Api::WhoReactedExists(item, Api::WhoReactedList::All);
|
||||
|
||||
AddReplyToMessageAction(result, request, list);
|
||||
if (item) {
|
||||
const auto media = item->media();
|
||||
const auto document = media ? media->document() : nullptr;
|
||||
const auto topic = item->topic();
|
||||
const auto peer = item->history()->peer.get();
|
||||
const auto canSendText = topic
|
||||
? Data::CanSendAnything(topic)
|
||||
: Data::CanSendAnything(peer);
|
||||
if (canSendText && document && document->isVoiceMessage()) {
|
||||
const auto msgId = item->fullId();
|
||||
if (const auto timecode = CurrentVoiceTimecode(msgId)) {
|
||||
const auto weak = base::make_weak(list.get());
|
||||
Menu::AddTimecodeAction(
|
||||
result,
|
||||
*timecode,
|
||||
VoiceTimecodeUpdates(msgId),
|
||||
[=] {
|
||||
const auto tc = CurrentVoiceTimecode(msgId);
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->insertTextAtCursor(
|
||||
tc.value_or(*timecode));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
AddTodoListAction(result, request, list);
|
||||
|
||||
if (request.overSelection
|
||||
|
||||
@@ -84,6 +84,9 @@ void AttachPollOptionTabs(
|
||||
not_null<Ui::PopupMenu*> menu,
|
||||
QPoint desiredPosition);
|
||||
|
||||
[[nodiscard]] std::optional<QString> CurrentVoiceTimecode(FullMsgId itemId);
|
||||
[[nodiscard]] rpl::producer<QString> VoiceTimecodeUpdates(FullMsgId itemId);
|
||||
|
||||
void AddPollActions(
|
||||
not_null<Ui::PopupMenu*> menu,
|
||||
not_null<PollData*> poll,
|
||||
|
||||
@@ -4522,6 +4522,16 @@ rpl::producer<FullMsgId> ListWidget::showMessageRequested() const {
|
||||
return _requestedToShowMessage.events();
|
||||
}
|
||||
|
||||
void ListWidget::setInsertTextCallback(Fn<void(QString)> callback) {
|
||||
_insertTextCallback = std::move(callback);
|
||||
}
|
||||
|
||||
void ListWidget::insertTextAtCursor(const QString &text) {
|
||||
if (_insertTextCallback) {
|
||||
_insertTextCallback(text);
|
||||
}
|
||||
}
|
||||
|
||||
void ListWidget::replyNextMessage(FullMsgId fullId, bool next) {
|
||||
const auto reply = [&](Element *view) {
|
||||
if (view) {
|
||||
|
||||
@@ -389,6 +389,8 @@ public:
|
||||
bool forceAnotherChat = false);
|
||||
[[nodiscard]] rpl::producer<FullMsgId> readMessageRequested() const;
|
||||
[[nodiscard]] rpl::producer<FullMsgId> showMessageRequested() const;
|
||||
void setInsertTextCallback(Fn<void(QString)> callback);
|
||||
void insertTextAtCursor(const QString &text);
|
||||
void replyNextMessage(FullMsgId fullId, bool next = true);
|
||||
|
||||
[[nodiscard]] Reactions::ButtonParameters reactionButtonParameters(
|
||||
@@ -899,6 +901,7 @@ private:
|
||||
rpl::event_stream<ReplyToMessageRequest> _requestedToReplyToMessage;
|
||||
rpl::event_stream<FullMsgId> _requestedToReadMessage;
|
||||
rpl::event_stream<FullMsgId> _requestedToShowMessage;
|
||||
Fn<void(QString)> _insertTextCallback;
|
||||
rpl::event_stream<not_null<QKeyEvent*>> _scrollKeyEvents;
|
||||
|
||||
[[nodiscard]] ElementOverlayHost &ensureOverlayHost();
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
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 "menu/menu_timecode_action.h"
|
||||
|
||||
#include "base/unique_qptr.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/widgets/fields/input_field.h"
|
||||
#include "ui/widgets/menu/menu_action.h"
|
||||
#include "ui/widgets/menu/menu_common.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class TimecodeAction final : public Ui::Menu::Action {
|
||||
public:
|
||||
TimecodeAction(
|
||||
not_null<Ui::Menu::Menu*> parent,
|
||||
const style::Menu &st,
|
||||
not_null<QAction*> action,
|
||||
const style::icon *icon,
|
||||
const QString &timecode,
|
||||
rpl::producer<QString> updates);
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
const style::icon *_replyIcon = nullptr;
|
||||
QString _timecode;
|
||||
|
||||
};
|
||||
|
||||
TimecodeAction::TimecodeAction(
|
||||
not_null<Ui::Menu::Menu*> parent,
|
||||
const style::Menu &st,
|
||||
not_null<QAction*> action,
|
||||
const style::icon *icon,
|
||||
const QString &timecode,
|
||||
rpl::producer<QString> updates)
|
||||
: Ui::Menu::Action(parent, st, action, nullptr, nullptr)
|
||||
, _replyIcon(icon)
|
||||
, _timecode(timecode) {
|
||||
std::move(updates) | rpl::on_next([=](const QString &value) {
|
||||
_timecode = value;
|
||||
update();
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
void TimecodeAction::paintEvent(QPaintEvent *e) {
|
||||
Ui::Menu::Action::paintEvent(e);
|
||||
|
||||
auto p = Painter(this);
|
||||
if (_replyIcon) {
|
||||
const auto pos = st().itemIconPosition;
|
||||
const auto iconW = _replyIcon->width();
|
||||
constexpr auto kScale = 0.8;
|
||||
const auto dx = pos.x() + (iconW * (1. - kScale)) / 2.;
|
||||
const auto dy = pos.y();
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.translate(dx, dy);
|
||||
p.scale(kScale, kScale);
|
||||
_replyIcon->paint(p, 0, 0, width() / kScale);
|
||||
p.resetTransform();
|
||||
}
|
||||
const auto &font = st::menuTimecodeFont;
|
||||
p.setFont(font);
|
||||
p.setPen(st::menuIconColor);
|
||||
const auto iconRight = st().itemIconPosition.x()
|
||||
+ st::menuIconReply.width();
|
||||
const auto textWidth = font->width(_timecode);
|
||||
const auto x = iconRight - textWidth;
|
||||
const auto y = st().itemIconPosition.y()
|
||||
+ st::menuIconReply.height()
|
||||
- font->descent;
|
||||
p.drawText(x, y, _timecode);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Menu {
|
||||
|
||||
void InsertTextAtCursor(
|
||||
not_null<Ui::InputField*> field,
|
||||
const QString &text) {
|
||||
auto cursor = field->textCursor();
|
||||
const auto pos = cursor.position();
|
||||
const auto doc = field->getTextWithTags().text;
|
||||
const auto needSpaceBefore = pos > 0
|
||||
&& !doc[pos - 1].isSpace();
|
||||
auto insert = QString();
|
||||
if (needSpaceBefore) {
|
||||
insert += ' ';
|
||||
}
|
||||
insert += text + ' ';
|
||||
cursor.insertText(insert);
|
||||
field->setTextCursor(cursor);
|
||||
}
|
||||
|
||||
not_null<QAction*> AddTimecodeAction(
|
||||
not_null<Ui::PopupMenu*> menu,
|
||||
const QString &timecode,
|
||||
rpl::producer<QString> updates,
|
||||
Fn<void()> callback) {
|
||||
auto item = base::make_unique_q<TimecodeAction>(
|
||||
menu->menu(),
|
||||
menu->st().menu,
|
||||
Ui::Menu::CreateAction(
|
||||
menu->menu().get(),
|
||||
tr::lng_context_reply_with_timecode(tr::now),
|
||||
std::move(callback)),
|
||||
&st::menuIconReply,
|
||||
timecode,
|
||||
std::move(updates));
|
||||
return menu->addAction(std::move(item));
|
||||
}
|
||||
|
||||
} // namespace Menu
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
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/basic_types.h"
|
||||
|
||||
class QAction;
|
||||
|
||||
namespace Ui {
|
||||
class InputField;
|
||||
class PopupMenu;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Menu {
|
||||
|
||||
void InsertTextAtCursor(
|
||||
not_null<Ui::InputField*> field,
|
||||
const QString &text);
|
||||
|
||||
not_null<QAction*> AddTimecodeAction(
|
||||
not_null<Ui::PopupMenu*> menu,
|
||||
const QString &timecode,
|
||||
rpl::producer<QString> updates,
|
||||
Fn<void()> callback);
|
||||
|
||||
} // namespace Menu
|
||||
@@ -808,6 +808,8 @@ popupMenuExpandedSeparator: PopupMenu(popupMenuWithIcons) {
|
||||
}
|
||||
}
|
||||
|
||||
menuTimecodeFont: font(7px semibold);
|
||||
|
||||
whoReadMenu: PopupMenu(popupMenuExpandedSeparator) {
|
||||
scrollPadding: margins(0px, 6px, 0px, 4px);
|
||||
maxHeight: 400px;
|
||||
|
||||
@@ -225,6 +225,8 @@ PRIVATE
|
||||
menu/menu_check_item.h
|
||||
menu/menu_item_rate_transcribe.cpp
|
||||
menu/menu_item_rate_transcribe.h
|
||||
menu/menu_timecode_action.cpp
|
||||
menu/menu_timecode_action.h
|
||||
menu/menu_ttl.cpp
|
||||
menu/menu_ttl.h
|
||||
|
||||
|
||||
Reference in New Issue
Block a user