From 0b2a5a22ba3e6449e069176189e39c45102a02a6 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 24 May 2022 19:38:46 +0400 Subject: [PATCH] Update API scheme on layer 143 + transcribe. --- Telegram/CMakeLists.txt | 4 + Telegram/Resources/tl/api.tl | 4 +- Telegram/SourceFiles/api/api_transcribes.cpp | 97 ++++++++++++++ Telegram/SourceFiles/api/api_transcribes.h | 48 +++++++ Telegram/SourceFiles/api/api_updates.cpp | 6 + Telegram/SourceFiles/apiwrap.cpp | 8 +- Telegram/SourceFiles/apiwrap.h | 3 + Telegram/SourceFiles/data/data_session.cpp | 16 ++- .../history/history_item_components.cpp | 20 +++ .../history/history_item_components.h | 24 ++-- .../view/history_view_transcribe_button.cpp | 58 +++++++++ .../view/history_view_transcribe_button.h | 38 ++++++ .../view/media/history_view_document.cpp | 121 ++++++++++++++++-- Telegram/SourceFiles/ui/chat/chat.style | 3 + 14 files changed, 421 insertions(+), 29 deletions(-) create mode 100644 Telegram/SourceFiles/api/api_transcribes.cpp create mode 100644 Telegram/SourceFiles/api/api_transcribes.h create mode 100644 Telegram/SourceFiles/history/view/history_view_transcribe_button.cpp create mode 100644 Telegram/SourceFiles/history/view/history_view_transcribe_button.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 83a2c4f30..862a44dbe 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -155,6 +155,8 @@ PRIVATE api/api_text_entities.h api/api_toggling_media.cpp api/api_toggling_media.h + api/api_transcribes.cpp + api/api_transcribes.h api/api_unread_things.cpp api/api_unread_things.h api/api_updates.cpp @@ -690,6 +692,8 @@ PRIVATE history/view/history_view_service_message.h history/view/history_view_spoiler_click_handler.cpp history/view/history_view_spoiler_click_handler.h + history/view/history_view_transcribe_button.cpp + history/view/history_view_transcribe_button.h history/view/history_view_top_bar_widget.cpp history/view/history_view_top_bar_widget.h history/view/history_view_view_button.cpp diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 57b1bad95..a8c92abae 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -390,6 +390,7 @@ updateAttachMenuBots#17b7a20b = Update; updateWebViewResultSent#1592b79d query_id:long = Update; updateBotMenuButton#14b85813 bot_id:long button:BotMenuButton = Update; updateSavedRingtones#74d8be99 = Update; +updateTranscribeAudio#88617090 flags:# final:flags.0?true transcription_id:long text:string = Update; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; @@ -1383,7 +1384,7 @@ inputInvoiceSlug#c326caef slug:string = InputInvoice; payments.exportedInvoice#aed0cbd9 url:string = payments.ExportedInvoice; -messages.transcribedAudio#bfcd7e0a transcription_id:long text:string = messages.TranscribedAudio; +messages.transcribedAudio#93752c52 flags:# pending:flags.0?true transcription_id:long text:string = messages.TranscribedAudio; ---functions--- @@ -1790,6 +1791,7 @@ payments.clearSavedInfo#d83d70c1 flags:# credentials:flags.0?true info:flags.1?t payments.getBankCardData#2e79d779 number:string = payments.BankCardData; payments.exportInvoice#f91b065 invoice_media:InputMedia = payments.ExportedInvoice; payments.assignAppStoreTransaction#6299a12f transaction_id:string = Updates; +payments.assignPlayMarketTransaction#4faa4aed purchase_token:string = Updates; stickers.createStickerSet#9021ab67 flags:# masks:flags.0?true animated:flags.1?true videos:flags.4?true user_id:InputUser title:string short_name:string thumb:flags.2?InputDocument stickers:Vector software:flags.3?string = messages.StickerSet; stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet; diff --git a/Telegram/SourceFiles/api/api_transcribes.cpp b/Telegram/SourceFiles/api/api_transcribes.cpp new file mode 100644 index 000000000..98755d934 --- /dev/null +++ b/Telegram/SourceFiles/api/api_transcribes.cpp @@ -0,0 +1,97 @@ +/* +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 "api/api_transcribes.h" + +#include "history/history_item.h" +#include "history/history.h" +#include "main/main_session.h" +#include "data/data_session.h" +#include "data/data_peer.h" +#include "apiwrap.h" + +namespace Api { + +Transcribes::Transcribes(not_null api) +: _session(&api->session()) +, _api(&api->instance()) { +} + +void Transcribes::toggle(not_null item) { + const auto id = item->fullId(); + auto i = _map.find(id); + if (i == _map.end()) { + load(item); + //_session->data().requestItemRepaint(item); + _session->data().requestItemResize(item); + } else if (!i->second.requestId) { + i->second.shown = !i->second.shown; + _session->data().requestItemResize(item); + } +} + +const Transcribes::Entry &Transcribes::entry( + not_null item) const { + static auto empty = Entry(); + const auto i = _map.find(item->fullId()); + return (i != _map.end()) ? i->second : empty; +} + +void Transcribes::apply(const MTPDupdateTranscribeAudio &update) { + const auto id = update.vtranscription_id().v; + const auto i = _ids.find(id); + if (i == _ids.end()) { + return; + } + const auto j = _map.find(i->second); + if (j == _map.end()) { + return; + } + const auto text = qs(update.vtext()); + j->second.result = text; + j->second.pending = !update.is_final(); + if (const auto item = _session->data().message(i->second)) { + _session->data().requestItemResize(item); + } +} + +void Transcribes::load(not_null item) { + if (!item->isHistoryEntry() || item->isLocal()) { + return; + } + const auto id = item->fullId(); + const auto requestId = _api.request(MTPmessages_TranscribeAudio( + item->history()->peer->input, + MTP_int(item->id) + )).done([=](const MTPmessages_TranscribedAudio &result) { + result.match([&](const MTPDmessages_transcribedAudio &data) { + auto &entry = _map[id]; + entry.requestId = 0; + entry.pending = data.is_pending(); + entry.result = qs(data.vtext()); + _ids.emplace(data.vtranscription_id().v, id); + if (const auto item = _session->data().message(id)) { + _session->data().requestItemResize(item); + } + }); + }).fail([=] { + auto &entry = _map[id]; + entry.requestId = 0; + entry.pending = false; + entry.failed = true; + if (const auto item = _session->data().message(id)) { + _session->data().requestItemResize(item); + } + }).send(); + auto &entry = _map.emplace(id).first->second; + entry.requestId = requestId; + entry.shown = true; + entry.failed = false; + entry.pending = false; +} + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_transcribes.h b/Telegram/SourceFiles/api/api_transcribes.h new file mode 100644 index 000000000..ef3bbfa9a --- /dev/null +++ b/Telegram/SourceFiles/api/api_transcribes.h @@ -0,0 +1,48 @@ +/* +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 "mtproto/sender.h" + +class ApiWrap; + +namespace Main { +class Session; +} // namespace Main + +namespace Api { + +class Transcribes final { +public: + explicit Transcribes(not_null api); + + struct Entry { + QString result; + bool shown = false; + bool failed = false; + bool pending = false; + mtpRequestId requestId = 0; + }; + + void toggle(not_null item); + [[nodiscard]] const Entry &entry(not_null item) const; + + void apply(const MTPDupdateTranscribeAudio &update); + +private: + void load(not_null item); + + const not_null _session; + MTP::Sender _api; + + base::flat_map _map; + base::flat_map _ids; + +}; + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp index 6d73fbc3f..fbd0afa03 100644 --- a/Telegram/SourceFiles/api/api_updates.cpp +++ b/Telegram/SourceFiles/api/api_updates.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_text_entities.h" #include "api/api_user_privacy.h" #include "api/api_unread_things.h" +#include "api/api_transcribes.h" #include "main/main_session.h" #include "main/main_account.h" #include "mtproto/mtp_instance.h" @@ -2387,6 +2388,11 @@ void Updates::feedUpdate(const MTPUpdate &update) { session().api().ringtones().applyUpdate(); } break; + case mtpc_updateTranscribeAudio: { + const auto &data = update.c_updateTranscribeAudio(); + _session->api().transcribes().apply(data); + } + } } diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index eb2493c5e..cc7f77775 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_confirm_phone.h" #include "api/api_unread_things.h" #include "api/api_ringtones.h" +#include "api/api_transcribes.h" #include "data/notify/data_notify_settings.h" #include "data/stickers/data_stickers.h" #include "data/data_drafts.h" @@ -145,7 +146,8 @@ ApiWrap::ApiWrap(not_null session) , _polls(std::make_unique(this)) , _chatParticipants(std::make_unique(this)) , _unreadThings(std::make_unique(this)) -, _ringtones(std::make_unique(this)) { +, _ringtones(std::make_unique(this)) +, _transcribes(std::make_unique(this)) { crl::on_main(session, [=] { // You can't use _session->lifetime() in the constructor, // only queued, because it is not constructed yet. @@ -4105,3 +4107,7 @@ Api::UnreadThings &ApiWrap::unreadThings() { Api::Ringtones &ApiWrap::ringtones() { return *_ringtones; } + +Api::Transcribes &ApiWrap::transcribes() { + return *_transcribes; +} diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 467a2828c..24d795c2b 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -70,6 +70,7 @@ class Polls; class ChatParticipants; class UnreadThings; class Ringtones; +class Transcribes; namespace details { @@ -361,6 +362,7 @@ public: [[nodiscard]] Api::ChatParticipants &chatParticipants(); [[nodiscard]] Api::UnreadThings &unreadThings(); [[nodiscard]] Api::Ringtones &ringtones(); + [[nodiscard]] Api::Transcribes &transcribes(); void updatePrivacyLastSeens(); @@ -644,6 +646,7 @@ private: const std::unique_ptr _chatParticipants; const std::unique_ptr _unreadThings; const std::unique_ptr _ringtones; + const std::unique_ptr _transcribes; mtpRequestId _wallPaperRequestId = 0; QString _wallPaperSlug; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index da5374f75..49352699b 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -63,6 +63,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_streaming.h" #include "data/data_media_rotation.h" #include "data/data_histories.h" +#include "data/data_peer_values.h" #include "base/platform/base_platform_info.h" #include "base/unixtime.h" #include "base/call_delayed.h" @@ -274,6 +275,20 @@ Session::Session(not_null session) session->saveSettingsDelayed(); } }, _lifetime); + + crl::on_main(_session, [=] { + AmPremiumValue( + _session + ) | rpl::start_with_next([=] { + for (const auto &[document, items] : _documentItems) { + if (document->isVoiceMessage()) { + for (const auto &item : items) { + requestItemResize(item); + } + } + } + }, _lifetime); + }); } void Session::clear() { @@ -1175,7 +1190,6 @@ void Session::setupPeerNameViewer() { const auto &oldLetters = update.oldFirstLetters; _contactsNoChatsList.peerNameChanged(peer, oldLetters); _contactsList.peerNameChanged(peer, oldLetters); - }, _lifetime); } diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp index 71def1223..cb6371c71 100644 --- a/Telegram/SourceFiles/history/history_item_components.cpp +++ b/Telegram/SourceFiles/history/history_item_components.cpp @@ -937,3 +937,23 @@ void HistoryDocumentVoice::stopSeeking() { _seeking = false; Media::Player::instance()->cancelSeeking(AudioMsgId::Type::Voice); } + +bool HistoryDocumentVoice::seeking() const { + return _seeking; +} + +float64 HistoryDocumentVoice::seekingStart() const { + return _seekingStart / kFloatToIntMultiplier; +} + +void HistoryDocumentVoice::setSeekingStart(float64 seekingStart) const { + _seekingStart = qRound(seekingStart * kFloatToIntMultiplier); +} + +float64 HistoryDocumentVoice::seekingCurrent() const { + return _seekingCurrent / kFloatToIntMultiplier; +} + +void HistoryDocumentVoice::setSeekingCurrent(float64 seekingCurrent) { + _seekingCurrent = qRound(seekingCurrent * kFloatToIntMultiplier); +} diff --git a/Telegram/SourceFiles/history/history_item_components.h b/Telegram/SourceFiles/history/history_item_components.h index 3b642dbe2..7beae9944 100644 --- a/Telegram/SourceFiles/history/history_item_components.h +++ b/Telegram/SourceFiles/history/history_item_components.h @@ -27,6 +27,7 @@ class Session; namespace HistoryView { class Element; class Document; +class TranscribeButton; } // namespace HistoryView struct HistoryMessageVia : public RuntimeComponent { @@ -444,23 +445,16 @@ public: std::shared_ptr _seekl; mutable int _lastDurationMs = 0; - bool seeking() const { - return _seeking; - } + [[nodiscard]] bool seeking() const; void startSeeking(); void stopSeeking(); - float64 seekingStart() const { - return _seekingStart / kFloatToIntMultiplier; - } - void setSeekingStart(float64 seekingStart) const { - _seekingStart = qRound(seekingStart * kFloatToIntMultiplier); - } - float64 seekingCurrent() const { - return _seekingCurrent / kFloatToIntMultiplier; - } - void setSeekingCurrent(float64 seekingCurrent) { - _seekingCurrent = qRound(seekingCurrent * kFloatToIntMultiplier); - } + [[nodiscard]] float64 seekingStart() const; + void setSeekingStart(float64 seekingStart) const; + [[nodiscard]] float64 seekingCurrent() const; + void setSeekingCurrent(float64 seekingCurrent); + + std::unique_ptr transcribe; + Ui::Text::String transcribeText; private: bool _seeking = false; diff --git a/Telegram/SourceFiles/history/view/history_view_transcribe_button.cpp b/Telegram/SourceFiles/history/view/history_view_transcribe_button.cpp new file mode 100644 index 000000000..5284f43c2 --- /dev/null +++ b/Telegram/SourceFiles/history/view/history_view_transcribe_button.cpp @@ -0,0 +1,58 @@ +/* +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_transcribe_button.h" + +#include "history/history.h" +#include "history/history_item.h" +#include "data/data_session.h" +#include "main/main_session.h" +#include "ui/chat/chat_style.h" +#include "ui/click_handler.h" +#include "api/api_transcribes.h" +#include "apiwrap.h" +#include "styles/style_chat.h" + +namespace HistoryView { + +TranscribeButton::TranscribeButton(not_null item) +: _item(item) { +} + +QSize TranscribeButton::size() const { + return QSize(st::historyTranscribeSize, st::historyTranscribeSize); +} + +void TranscribeButton::paint( + QPainter &p, + int x, + int y, + const PaintContext &context) { + auto hq = PainterHighQualityEnabler(p); + const auto stm = context.messageStyle(); + p.setBrush(stm->msgWaveformInactive); + const auto radius = size().width() / 4; + p.drawRoundedRect(QRect{ QPoint(x, y), size() }, radius, radius); +} + +ClickHandlerPtr TranscribeButton::link() { + if (!_item->isHistoryEntry() || _item->isLocal()) { + return nullptr; + } else if (_link) { + return _link; + } + const auto session = &_item->history()->session(); + const auto id = _item->fullId(); + _link = std::make_shared([=] { + if (const auto item = session->data().message(id)) { + session->api().transcribes().toggle(item); + } + }); + return _link; +} + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_transcribe_button.h b/Telegram/SourceFiles/history/view/history_view_transcribe_button.h new file mode 100644 index 000000000..de506cde0 --- /dev/null +++ b/Telegram/SourceFiles/history/view/history_view_transcribe_button.h @@ -0,0 +1,38 @@ +/* +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 + +namespace Ui { +struct ChatPaintContext; +} // namespace Ui + +namespace HistoryView { + +using PaintContext = Ui::ChatPaintContext; + +class TranscribeButton final { +public: + explicit TranscribeButton(not_null item); + + [[nodiscard]] QSize size() const; + + void paint(QPainter &p, int x, int y, const PaintContext &context); + + [[nodiscard]] ClickHandlerPtr link(); + +private: + const not_null _item; + + ClickHandlerPtr _link; + QString _text; + bool _loaded = false; + bool _loading = false; + +}; + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index 20e58021c..5181676fd 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -9,12 +9,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "storage/localstorage.h" +#include "main/main_session.h" #include "media/audio/media_audio.h" #include "media/player/media_player_instance.h" #include "history/history_item_components.h" #include "history/history.h" #include "history/view/history_view_element.h" #include "history/view/history_view_cursor_state.h" +#include "history/view/history_view_transcribe_button.h" #include "history/view/media/history_view_media_common.h" #include "ui/image/image.h" #include "ui/text/format_values.h" @@ -30,6 +32,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_media_types.h" #include "data/data_file_click_handler.h" #include "data/data_file_origin.h" +#include "api/api_transcribes.h" +#include "apiwrap.h" #include "styles/style_chat.h" namespace HistoryView { @@ -226,6 +230,41 @@ void Document::fillNamedFromData(HistoryDocumentNamed *named) { QSize Document::countOptimalSize() { auto captioned = Get(); + auto hasTranscribe = false; + const auto voice = Get(); + if (voice) { + const auto session = &_realParent->history()->session(); + if (!session->premium()) { + voice->transcribe = nullptr; + voice->transcribeText = {}; + } else { + if (!voice->transcribe) { + voice->transcribe = std::make_unique( + _realParent); + } + const auto &entry = session->api().transcribes().entry( + _realParent); + auto text = entry.requestId + ? "Transcribing..." + : entry.failed + ? "Transcribing Failed." + : entry.shown + ? ((entry.pending ? "Still Transcribing...\n" : "") + + entry.result) + : QString(); + if (text.isEmpty()) { + voice->transcribeText = {}; + } else { + const auto minResizeWidth = st::minPhotoSize + - st::msgPadding.left() + - st::msgPadding.right(); + voice->transcribeText = Ui::Text::String(minResizeWidth); + voice->transcribeText.setText(st::messageTextStyle, text); + hasTranscribe = true; + } + } + } + if (_parent->media() != this && !_realParent->groupId()) { if (captioned) { RemoveComponents(HistoryDocumentCaptioned::Bit()); @@ -236,6 +275,7 @@ QSize Document::countOptimalSize() { _parent->skipBlockWidth(), _parent->skipBlockHeight()); } + auto thumbed = Get(); const auto &st = thumbed ? st::msgFileThumbLayout : st::msgFileLayout; if (thumbed) { @@ -264,15 +304,30 @@ QSize Document::countOptimalSize() { accumulate_max(maxWidth, tleft + named->_namew + tright); accumulate_min(maxWidth, st::msgMaxWidth); } + if (voice && voice->transcribe) { + maxWidth += st::historyTranscribeSkip + + voice->transcribe->size().width(); + } auto minHeight = st.padding.top() + st.thumbSize + st.padding.bottom(); - if (!captioned && _parent->bottomInfoIsWide()) { + if (!captioned && !hasTranscribe && _parent->bottomInfoIsWide()) { minHeight += st::msgDateFont->height - st::msgDateDelta.y(); } if (!isBubbleTop()) { minHeight -= st::msgFileTopMinus; } + if (hasTranscribe) { + auto captionw = maxWidth + - st::msgPadding.left() + - st::msgPadding.right(); + minHeight += voice->transcribeText.countHeight(captionw); + if (captioned) { + minHeight += st::mediaCaptionSkip; + } else if (isBubbleBottom()) { + minHeight += st::msgPadding.bottom(); + } + } if (captioned) { auto captionw = maxWidth - st::msgPadding.left() @@ -287,7 +342,9 @@ QSize Document::countOptimalSize() { QSize Document::countCurrentSize(int newWidth) { const auto captioned = Get(); - if (!captioned) { + const auto voice = Get(); + const auto hasTranscribe = voice && !voice->transcribeText.isEmpty(); + if (!captioned && !hasTranscribe) { return File::countCurrentSize(newWidth); } @@ -299,9 +356,19 @@ QSize Document::countCurrentSize(int newWidth) { newHeight -= st::msgFileTopMinus; } auto captionw = newWidth - st::msgPadding.left() - st::msgPadding.right(); - newHeight += captioned->_caption.countHeight(captionw); - if (isBubbleBottom()) { - newHeight += st::msgPadding.bottom(); + if (hasTranscribe) { + newHeight += voice->transcribeText.countHeight(captionw); + if (captioned) { + newHeight += st::mediaCaptionSkip; + } else if (isBubbleBottom()) { + newHeight += st::msgPadding.bottom(); + } + } + if (captioned) { + newHeight += captioned->_caption.countHeight(captionw); + if (isBubbleBottom()) { + newHeight += st::msgPadding.bottom(); + } } return { newWidth, newHeight }; @@ -498,7 +565,8 @@ void Document::draw( auto statuswidth = namewidth; auto voiceStatusOverride = QString(); - if (const auto voice = Get()) { + const auto voice = Get(); + if (voice) { ensureDataMediaCreated(); if (const auto voiceData = _data->voice()) { @@ -527,9 +595,19 @@ void Document::draw( base::SafeRound(progress * voice->_lastDurationMs) / 1000, voice->_lastDurationMs / 1000); } - + if (voice->transcribe) { + const auto size = voice->transcribe->size(); + namewidth -= st::historyTranscribeSkip + size.width(); + const auto x = nameleft + namewidth + st::historyTranscribeSkip; + const auto y = st.padding.top() + - topMinus + + st::msgWaveformMax + - size.height(); + voice->transcribe->paint(p, x, y, context); + } p.save(); p.translate(nameleft, st.padding.top() - topMinus); + PaintWaveform(p, context, _data->voice(), @@ -564,9 +642,15 @@ void Document::draw( } } + auto captiontop = bottom; + if (voice && !voice->transcribeText.isEmpty()) { + p.setPen(stm->historyTextFg); + voice->transcribeText.draw(p, st::msgPadding.left(), bottom, captionw, style::al_left, 0, -1, context.selection); + captiontop += voice->transcribeText.countHeight(captionw) + st::mediaCaptionSkip; + } if (auto captioned = Get()) { p.setPen(stm->historyTextFg); - captioned->_caption.draw(p, st::msgPadding.left(), bottom, captionw, style::al_left, 0, -1, context.selection); + captioned->_caption.draw(p, st::msgPadding.left(), captiontop, captionw, style::al_left, 0, -1, context.selection); } } @@ -745,9 +829,23 @@ TextState Document::textState( } } - if (const auto voice = Get()) { - auto namewidth = width - nameleft - nameright; + const auto voice = Get(); + auto namewidth = width - nameleft - nameright; + if (voice) { auto waveformbottom = st.padding.top() - topMinus + st::msgWaveformMax + st::msgWaveformMin; + if (voice->transcribe) { + const auto size = voice->transcribe->size(); + namewidth -= st::historyTranscribeSkip + size.width(); + const auto x = nameleft + namewidth + st::historyTranscribeSkip; + const auto y = st.padding.top() + - topMinus + + st::msgWaveformMax + - size.height(); + if (QRect(QPoint(x, y), size).contains(point)) { + result.link = voice->transcribe->link(); + return result; + } + } if (QRect(nameleft, nametop, namewidth, waveformbottom - nametop).contains(point)) { const auto state = ::Media::Player::instance()->getState(AudioMsgId::Type::Voice); if (state.id == AudioMsgId(_data, _realParent->fullId(), state.id.externalPlayId()) @@ -776,7 +874,8 @@ TextState Document::textState( painth -= st::msgPadding.bottom(); } } - if (QRect(0, 0, width, painth).contains(point) + const auto till = voice ? (nameleft + namewidth) : width; + if (QRect(0, 0, till, painth).contains(point) && (!_data->loading() || downloadInCorner()) && !_data->uploading() && !_data->isNull()) { diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index ba3942cac..754fc17dd 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -655,6 +655,9 @@ msgWaveformSkip: 1px; msgWaveformMin: 2px; msgWaveformMax: 20px; +historyTranscribeSkip: 10px; +historyTranscribeSize: 24px; + historyVideoMessageMute: icon {{ "volume_mute", historyFileThumbIconFg }}; historyVideoMessageMuteSelected: icon {{ "volume_mute", historyFileThumbIconFgSelected }}; historyVideoMessageMuteSize: 25px;