Added initial support of trial voice transcribes.

This commit is contained in:
23rd 2023-11-29 07:29:27 +03:00
parent a546b3a9b6
commit 27b284ef5b
13 changed files with 240 additions and 20 deletions

View File

@ -3335,6 +3335,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_audio_player_reverse" = "Reverse order";
"lng_audio_player_shuffle" = "Shuffle";
"lng_audio_transcribe_long" = "This voice message is too long.";
"lng_audio_transcribe_trials_left#one" = "You have {count} free transcription left until {date}.";
"lng_audio_transcribe_trials_left#other" = "You have {count} free transcriptions left until {date}.";
"lng_audio_transcribe_trials_over" = "You have used all your free transcriptions this week. Wait until {date} to use it again or subscribe to {link} now.";
"lng_rights_edit_admin" = "Manage permissions";
"lng_rights_edit_admin_header" = "What can this admin do?";

View File

@ -7,13 +7,16 @@ 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_document.h"
#include "data/data_session.h"
#include "data/data_peer.h"
#include "apiwrap.h"
#include "data/data_document.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "history/history.h"
#include "history/history_item.h"
#include "history/history_item_helpers.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "main/main_session.h"
namespace Api {
@ -22,6 +25,44 @@ Transcribes::Transcribes(not_null<ApiWrap*> api)
, _api(&api->instance()) {
}
bool Transcribes::trialsSupport() {
if (!_trialsSupport) {
const auto count = _session->account().appConfig().get<int>(
u"transcribe_audio_trial_weekly_number"_q,
0);
const auto until = _session->account().appConfig().get<int>(
u"transcribe_audio_trial_cooldown_until"_q,
0);
_trialsSupport = (count > 0) || (until > 0);
}
return *_trialsSupport;
}
TimeId Transcribes::trialsRefreshAt() {
if (_trialsRefreshAt < 0) {
_trialsRefreshAt = _session->account().appConfig().get<int>(
u"transcribe_audio_trial_cooldown_until"_q,
0);
}
return _trialsRefreshAt;
}
int Transcribes::trialsCount() {
if (_trialsCount < 0) {
_trialsCount = _session->account().appConfig().get<int>(
u"transcribe_audio_trial_weekly_number"_q,
-1);
return std::max(_trialsCount, 0);
}
return _trialsCount;
}
crl::time Transcribes::trialsMaxLengthMs() const {
return 1000 * _session->account().appConfig().get<int>(
u"transcribe_audio_trial_duration_max"_q,
300);
}
void Transcribes::toggle(not_null<HistoryItem*> item) {
const auto id = item->fullId();
auto i = _map.find(id);
@ -86,6 +127,23 @@ void Transcribes::load(not_null<HistoryItem*> item) {
MTP_int(item->id)
)).done([=](const MTPmessages_TranscribedAudio &result) {
const auto &data = result.data();
{
const auto trialsCountChanged = data.vtrial_remains_num()
&& (_trialsCount != data.vtrial_remains_num()->v);
if (trialsCountChanged) {
_trialsCount = data.vtrial_remains_num()->v;
}
const auto refreshAtChanged = data.vtrial_remains_until_date()
&& (_trialsRefreshAt != data.vtrial_remains_until_date()->v);
if (refreshAtChanged) {
_trialsRefreshAt = data.vtrial_remains_until_date()->v;
}
if (trialsCountChanged) {
ShowTrialTranscribesToast(_trialsCount, _trialsRefreshAt);
}
}
auto &entry = _map[id];
entry.requestId = 0;
entry.pending = data.is_pending();

View File

@ -36,12 +36,21 @@ public:
void apply(const MTPDupdateTranscribedAudio &update);
[[nodiscard]] bool trialsSupport();
[[nodiscard]] TimeId trialsRefreshAt();
[[nodiscard]] int trialsCount();
[[nodiscard]] crl::time trialsMaxLengthMs() const;
private:
void load(not_null<HistoryItem*> item);
const not_null<Main::Session*> _session;
MTP::Sender _api;
int _trialsCount = -1;
std::optional<bool> _trialsSupport;
TimeId _trialsRefreshAt = -1;
base::flat_map<FullMsgId, Entry> _map;
base::flat_map<uint64, FullMsgId> _ids;

View File

@ -11,20 +11,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/premium_preview_box.h"
#include "calls/calls_instance.h"
#include "data/notify/data_notify_settings.h"
#include "data/data_chat_participant_status.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_changes.h"
#include "data/data_group_call.h"
#include "data/data_forum.h"
#include "data/data_forum_topic.h"
#include "data/data_media_types.h"
#include "data/data_message_reactions.h"
#include "data/data_session.h"
#include "data/data_stories.h"
#include "data/data_user.h"
#include "history/history.h"
#include "history/history_item.h"
#include "history/history_item_components.h"
#include "main/main_account.h"
#include "main/main_domain.h"
@ -39,7 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/click_handler_types.h" // ClickHandlerContext.
#include "ui/text/format_values.h"
#include "ui/text/text_utilities.h"
#include "ui/text/text_entity.h"
#include "ui/toast/toast.h"
#include "ui/item_text_options.h"
#include "lang/lang_keys.h"
@ -744,3 +741,39 @@ void CheckReactionNotificationSchedule(
EntityInText(EntityType::Italic, 0, result.text.size()));
return result;
}
void ShowTrialTranscribesToast(int left, TimeId until) {
const auto window = Core::App().activeWindow();
if (!window) {
return;
}
const auto filter = [=](const auto &...) {
if (const auto controller = window->sessionController()) {
ShowPremiumPreviewBox(controller, PremiumPreview::VoiceToText);
window->activate();
}
return false;
};
const auto date = langDateTime(base::unixtime::parse(until));
constexpr auto kToastDuration = crl::time(4000);
const auto text = left
? tr::lng_audio_transcribe_trials_left(
tr::now,
lt_count,
left,
lt_date,
{ date },
Ui::Text::WithEntities)
: tr::lng_audio_transcribe_trials_over(
tr::now,
lt_date,
Ui::Text::Bold(date),
lt_link,
Ui::Text::Link(tr::lng_settings_privacy_premium_link(tr::now)),
Ui::Text::WithEntities);
window->uiShow()->showToast(Ui::Toast::Config{
.text = text,
.duration = kToastDuration,
.filter = filter,
});
}

View File

@ -153,3 +153,5 @@ ClickHandlerPtr JumpToStoryClickHandler(
[[nodiscard]] ClickHandlerPtr GroupCallClickHandler(
not_null<PeerData*> peer,
CallId callId);
void ShowTrialTranscribesToast(int left, TimeId until);

View File

@ -7,18 +7,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/view/history_view_transcribe_button.h"
#include "base/unixtime.h"
#include "boxes/premium_preview_box.h"
#include "core/click_handler_types.h" // ClickHandlerContext
#include "history/history.h"
#include "history/history_item.h"
#include "data/data_document.h"
#include "data/data_session.h"
#include "main/main_session.h"
#include "lang/lang_keys.h"
#include "ui/chat/chat_style.h"
#include "ui/click_handler.h"
#include "ui/effects/radial_animation.h"
#include "ui/effects/ripple_animation.h"
#include "ui/painter.h"
#include "ui/rect.h"
#include "api/api_transcribes.h"
#include "apiwrap.h"
#include "styles/style_chat.h"
#include "window/window_session_controller.h"
namespace HistoryView {
namespace {
@ -26,6 +32,21 @@ namespace {
constexpr auto kInNonChosenOpacity = 0.12;
constexpr auto kOutNonChosenOpacity = 0.18;
void ClipPainterForLock(QPainter &p, bool roundview, const QRect &r) {
const auto &pos = roundview
? st::historyFastTranscribeLockOverlayPos
: st::historyTranscribeLockOverlayPos;
const auto &size = roundview
? st::historyFastTranscribeLockOverlaySize
: st::historyTranscribeLockOverlaySize;
auto clipPath = QPainterPath();
clipPath.addRect(r);
const auto clear = QRect(pos + r.topLeft(), size);
clipPath.addRoundedRect(clear, clear.width() * 0.5, clear.height() * 0.5);
p.setClipPath(clipPath);
}
} // namespace
TranscribeButton::TranscribeButton(
@ -84,12 +105,22 @@ void TranscribeButton::paint(
}
}
PainterHighQualityEnabler hq(p);
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(context.st->msgServiceBg());
p.drawEllipse(r);
context.st->historyFastTranscribeIcon().paintInCenter(p, r);
if (!_loading && hasLock()) {
ClipPainterForLock(p, true, r);
context.st->historyFastTranscribeIcon().paintInCenter(p, r);
p.setClipping(false);
context.st->historyFastTranscribeLock().paint(
p,
r.topLeft() + st::historyFastTranscribeLockPos,
r.width());
} else {
context.st->historyFastTranscribeIcon().paintInCenter(p, r);
}
const auto state = _animation
? _animation->computeState()
@ -169,7 +200,19 @@ void TranscribeButton::paint(
p.scale(1. - opened, 1. - opened);
p.translate(-r.center());
}
stm->historyTranscribeIcon.paintInCenter(p, r);
if (!_loading && hasLock()) {
ClipPainterForLock(p, false, r);
stm->historyTranscribeIcon.paintInCenter(p, r);
p.setClipping(false);
stm->historyTranscribeLock.paint(
p,
r.topLeft() + st::historyTranscribeLockPos,
r.width());
} else {
stm->historyTranscribeIcon.paintInCenter(p, r);
}
if (opened != 0.) {
p.restore();
}
@ -177,6 +220,21 @@ void TranscribeButton::paint(
p.setOpacity(1.);
}
bool TranscribeButton::hasLock() const {
if (_item->history()->session().premium()) {
return false;
}
if (_item->history()->session().api().transcribes().trialsCount()) {
return false;
}
const auto until = _item->history()->session().api().transcribes()
.trialsRefreshAt();
if (!until || base::unixtime::now() >= until) {
return false;
}
return true;
}
void TranscribeButton::setOpened(bool opened, Fn<void()> update) {
if (_opened == opened) {
return;
@ -201,8 +259,35 @@ ClickHandlerPtr TranscribeButton::link() {
}
const auto session = &_item->history()->session();
const auto id = _item->fullId();
_link = std::make_shared<LambdaClickHandler>([=] {
if (const auto item = session->data().message(id)) {
_link = std::make_shared<LambdaClickHandler>([=](ClickContext context) {
const auto item = session->data().message(id);
if (!item) {
return;
}
if (session->premium()) {
return session->api().transcribes().toggle(item);
}
const auto my = context.other.value<ClickHandlerContext>();
if (hasLock()) {
if (const auto controller = my.sessionWindow.get()) {
ShowPremiumPreviewBox(
controller,
PremiumPreview::VoiceToText);
}
} else {
const auto max = session->api().transcribes().trialsMaxLengthMs();
const auto doc = _item->media()
? _item->media()->document()
: nullptr;
if (doc && (doc->isVoiceMessage() || doc->isVideoMessage())) {
if (doc->duration() > max) {
if (const auto controller = my.sessionWindow.get()) {
controller->uiShow()->showToast(
tr::lng_audio_transcribe_long(tr::now));
return;
}
}
}
session->api().transcribes().toggle(item);
}
});

View File

@ -36,6 +36,8 @@ public:
[[nodiscard]] bool contains(const QPoint &p);
private:
[[nodiscard]] bool hasLock() const;
const not_null<HistoryItem*> _item;
const bool _roundview = false;
const QSize _size;

View File

@ -306,7 +306,7 @@ QSize Document::countOptimalSize() {
const auto voice = Get<HistoryDocumentVoice>();
if (voice) {
const auto session = &_realParent->history()->session();
if (!session->premium()) {
if (!session->premium() && !session->api().transcribes().trialsSupport()) {
voice->transcribe = nullptr;
voice->transcribeText = {};
} else {

View File

@ -1973,7 +1973,9 @@ bool Gif::needCornerStatusDisplay() const {
}
void Gif::ensureTranscribeButton() const {
if (_data->isVideoMessage() && _data->session().premium()) {
if (_data->isVideoMessage()
&& (_data->session().premium()
|| _data->session().api().transcribes().trialsSupport())) {
if (!_transcribe) {
_transcribe = std::make_unique<TranscribeButton>(
_realParent,

View File

@ -9,8 +9,8 @@
#include "lang/lang_keys.h"
#include "ui/abstract_button.h"
#include "ui/effects/shake_animation.h"
#include "ui/painter.h"
#include "ui/rect.h"
#include "ui/text/text_entity.h"
#include "ui/widgets/popup_menu.h"
#include "styles/style_intro.h"
#include "styles/style_layers.h" // boxRadius
@ -127,7 +127,10 @@ void CodeDigit::paintEvent(QPaintEvent *e) {
p.setClipPath(clipPath);
p.fillRect(rect(), st::windowBgOver);
p.strokePath(clipPath, _borderPen);
{
auto hq = PainterHighQualityEnabler(p);
p.strokePath(clipPath, _borderPen);
}
if (_viewDigit == kDigitNone) {
return;

View File

@ -522,6 +522,13 @@ historyTranscribeInHide: icon {{ "chat/voice_to_text_collapse", msgFileInBg }};
historyTranscribeInHideSelected: icon {{ "chat/voice_to_text_collapse", msgFileInBgSelected }};
historyTranscribeOutHide: icon {{ "chat/voice_to_text_collapse", msgFileOutBg }};
historyTranscribeOutHideSelected: icon {{ "chat/voice_to_text_collapse", msgFileOutBgSelected }};
historyTranscribeInLock: icon {{ "chat/mini_lock", msgFileInBg }};
historyTranscribeInLockSelected: icon {{ "chat/mini_lock", msgFileInBgSelected }};
historyTranscribeOutLock: icon {{ "chat/mini_lock", msgFileOutBg }};
historyTranscribeOutLockSelected: icon {{ "chat/mini_lock", msgFileOutBgSelected }};
historyTranscribeLockPos: point(17px, 9px);
historyTranscribeLockOverlayPos: point(19px, 11px);
historyTranscribeLockOverlaySize: size(5px, 10px);
historyVideoMessageMute: icon {{ "volume_mute", historyFileThumbIconFg }};
historyVideoMessageMuteSelected: icon {{ "volume_mute", historyFileThumbIconFgSelected }};
@ -562,6 +569,10 @@ historyFastCommentsIcon: icon {{ "fast_comments", msgServiceFg }};
historyFastCloseSize: 30px;
historyFastCloseIcon: icon {{ "box_button_close", msgServiceFg }};
historyFastTranscribeIcon: icon {{ "chat/voice_to_text", msgServiceFg }};
historyFastTranscribeLock: icon {{ "chat/mini_lock", msgServiceFg }};
historyFastTranscribeLockPos: point(18px, 13px);
historyFastTranscribeLockOverlayPos: point(21px, 13px);
historyFastTranscribeLockOverlaySize: size(6px, 10px);
historySavedFont: font(semibold 14px);

View File

@ -189,6 +189,7 @@ ChatStyle::ChatStyle(rpl::producer<ColorIndicesCompressed> colorIndices) {
make(_historyFastCommentsIcon, st::historyFastCommentsIcon);
make(_historyFastShareIcon, st::historyFastShareIcon);
make(_historyFastTranscribeIcon, st::historyFastTranscribeIcon);
make(_historyFastTranscribeLock, st::historyFastTranscribeLock);
make(_historyGoToOriginalIcon, st::historyGoToOriginalIcon);
make(_historyFastCloseIcon, st::historyFastCloseIcon);
make(_historyMapPoint, st::historyMapPoint);
@ -467,6 +468,12 @@ ChatStyle::ChatStyle(rpl::producer<ColorIndicesCompressed> colorIndices) {
st::historyTranscribeInIconSelected,
st::historyTranscribeOutIcon,
st::historyTranscribeOutIconSelected);
make(
&MessageStyle::historyTranscribeLock,
st::historyTranscribeInLock,
st::historyTranscribeInLockSelected,
st::historyTranscribeOutLock,
st::historyTranscribeOutLockSelected);
make(
&MessageStyle::historyTranscribeHide,
st::historyTranscribeInHide,

View File

@ -89,6 +89,7 @@ struct MessageStyle {
style::icon historyPollChosen = { Qt::Uninitialized };
style::icon historyPollChoiceRight = { Qt::Uninitialized };
style::icon historyTranscribeIcon = { Qt::Uninitialized };
style::icon historyTranscribeLock = { Qt::Uninitialized };
style::icon historyTranscribeHide = { Qt::Uninitialized };
std::array<
std::unique_ptr<Text::QuotePaintCache>,
@ -385,6 +386,9 @@ public:
[[nodiscard]] const style::icon &historyFastTranscribeIcon() const {
return _historyFastTranscribeIcon;
}
[[nodiscard]] const style::icon &historyFastTranscribeLock() const {
return _historyFastTranscribeLock;
}
[[nodiscard]] const style::icon &historyGoToOriginalIcon() const {
return _historyGoToOriginalIcon;
}
@ -514,6 +518,7 @@ private:
style::icon _historyFastCommentsIcon = { Qt::Uninitialized };
style::icon _historyFastShareIcon = { Qt::Uninitialized };
style::icon _historyFastTranscribeIcon = { Qt::Uninitialized };
style::icon _historyFastTranscribeLock = { Qt::Uninitialized };
style::icon _historyGoToOriginalIcon = { Qt::Uninitialized };
style::icon _historyFastCloseIcon = { Qt::Uninitialized };
style::icon _historyMapPoint = { Qt::Uninitialized };