mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-14 13:17:08 +02:00
Added initial support of trial voice transcribes.
This commit is contained in:
parent
a546b3a9b6
commit
27b284ef5b
13 changed files with 240 additions and 20 deletions
|
@ -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?";
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -153,3 +153,5 @@ ClickHandlerPtr JumpToStoryClickHandler(
|
|||
[[nodiscard]] ClickHandlerPtr GroupCallClickHandler(
|
||||
not_null<PeerData*> peer,
|
||||
CallId callId);
|
||||
|
||||
void ShowTrialTranscribesToast(int left, TimeId until);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 };
|
||||
|
|
Loading…
Add table
Reference in a new issue