Added initial support for voice messages with TTL.

This commit is contained in:
23rd 2023-12-29 04:07:04 +03:00 committed by John Preston
parent c2712b0104
commit 85286684e3
10 changed files with 144 additions and 37 deletions

View file

@ -1715,6 +1715,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_ttl_video_received" = "{from} sent you a self-destructing video. Please view it on your mobile.";
"lng_ttl_video_sent" = "You sent a self-destructing video.";
"lng_ttl_video_expired" = "Video has expired";
"lng_ttl_voice_sent" = "You sent a self-destructing voice messsage.";
"lng_ttl_voice_expired" = "Voice message expired";
"lng_ttl_round_sent" = "You sent a self-destructing video message.";
"lng_ttl_round_expired" = "Round message expired";
"lng_profile_add_more_after_create" = "You will be able to add more members after you create the group.";
"lng_profile_camera_title" = "Capture yourself";

View file

@ -555,6 +555,10 @@ bool Media::hasSpoiler() const {
return false;
}
crl::time Media::ttlSeconds() const {
return 0;
}
bool Media::consumeMessageText(const TextWithEntities &text) {
return false;
}
@ -849,12 +853,14 @@ MediaFile::MediaFile(
not_null<HistoryItem*> parent,
not_null<DocumentData*> document,
bool skipPremiumEffect,
bool spoiler)
bool spoiler,
crl::time ttlSeconds)
: Media(parent)
, _document(document)
, _emoji(document->sticker() ? document->sticker()->alt : QString())
, _skipPremiumEffect(skipPremiumEffect)
, _spoiler(spoiler) {
, _spoiler(spoiler)
, _ttlSeconds(ttlSeconds) {
parent->history()->owner().registerDocumentItem(_document, parent);
if (!_emoji.isEmpty()) {
@ -882,7 +888,8 @@ std::unique_ptr<Media> MediaFile::clone(not_null<HistoryItem*> parent) {
parent,
_document,
!_document->session().premium(),
_spoiler);
_spoiler,
_ttlSeconds);
}
DocumentData *MediaFile::document() const {
@ -1129,6 +1136,10 @@ bool MediaFile::hasSpoiler() const {
return _spoiler;
}
crl::time MediaFile::ttlSeconds() const {
return _ttlSeconds;
}
bool MediaFile::updateInlineResultMedia(const MTPMessageMedia &media) {
if (media.type() != mtpc_messageMediaDocument) {
return false;

View file

@ -174,6 +174,7 @@ public:
virtual bool dropForwardedInfo() const;
virtual bool forceForwardedInfo() const;
[[nodiscard]] virtual bool hasSpoiler() const;
[[nodiscard]] virtual crl::time ttlSeconds() const;
[[nodiscard]] virtual bool consumeMessageText(
const TextWithEntities &text);
@ -256,7 +257,8 @@ public:
not_null<HistoryItem*> parent,
not_null<DocumentData*> document,
bool skipPremiumEffect,
bool spoiler);
bool spoiler,
crl::time ttlSeconds);
~MediaFile();
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
@ -278,6 +280,7 @@ public:
bool forwardedBecomesUnread() const override;
bool dropForwardedInfo() const override;
bool hasSpoiler() const override;
crl::time ttlSeconds() const override;
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
bool updateSentMedia(const MTPMessageMedia &media) override;
@ -292,6 +295,9 @@ private:
bool _skipPremiumEffect = false;
bool _spoiler = false;
// Video (unsupported) / Voice / Round.
crl::time _ttlSeconds = 0;
};
class MediaContact final : public Media {

View file

@ -242,7 +242,7 @@ std::unique_ptr<Data::Media> HistoryItem::CreateMedia(
});
}, [&](const MTPDmessageMediaDocument &media) -> Result {
const auto document = media.vdocument();
if (media.vttl_seconds()) {
if (media.vttl_seconds() && media.is_video()) {
LOG(("App Error: "
"Unexpected MTPMessageMediaDocument "
"with ttl_seconds in CreateMedia."));
@ -258,7 +258,8 @@ std::unique_ptr<Data::Media> HistoryItem::CreateMedia(
item,
item->history()->owner().processDocument(document),
media.is_nopremium(),
media.is_spoiler());
media.is_spoiler(),
media.vttl_seconds().value_or_empty());
}, [](const MTPDdocumentEmpty &) -> Result {
return nullptr;
});
@ -354,7 +355,8 @@ HistoryItem::HistoryItem(
setServiceText({
tr::lng_message_empty(tr::now, Ui::Text::WithEntities)
});
} else if (checked == MediaCheckResult::HasTimeToLive) {
} else if ((checked == MediaCheckResult::HasUnsupportedTimeToLive)
|| (checked == MediaCheckResult::HasExpiredMediaTimeToLive)) {
createServiceFromMtp(data);
applyTTL(data);
} else if (checked == MediaCheckResult::HasStoryMention) {
@ -617,7 +619,8 @@ HistoryItem::HistoryItem(
this,
document,
skipPremiumEffect,
spoiler);
spoiler,
/*ttlSeconds = */0);
setText(caption);
}
@ -1659,7 +1662,8 @@ void HistoryItem::setStoryFields(not_null<Data::Story*> story) {
this,
document,
/*skipPremiumEffect=*/false,
spoiler);
spoiler,
/*ttlSeconds = */0);
}
setText(story->caption());
}
@ -3605,25 +3609,43 @@ void HistoryItem::createServiceFromMtp(const MTPDmessage &message) {
const auto ttl = data.vttl_seconds();
Assert(ttl != nullptr);
setSelfDestruct(HistoryServiceSelfDestruct::Type::Video, *ttl);
if (out()) {
setServiceText({
tr::lng_ttl_video_sent(tr::now, Ui::Text::WithEntities)
});
} else {
auto result = PreparedServiceText();
result.links.push_back(fromLink());
result.text = tr::lng_ttl_video_received(
tr::now,
lt_from,
fromLinkText(), // Link 1.
Ui::Text::WithEntities);
setServiceText(std::move(result));
if (data.is_video()) {
setSelfDestruct(
HistoryServiceSelfDestruct::Type::Video,
*ttl);
if (out()) {
setServiceText({
tr::lng_ttl_video_sent(
tr::now,
Ui::Text::WithEntities)
});
} else {
auto result = PreparedServiceText();
result.links.push_back(fromLink());
result.text = tr::lng_ttl_video_received(
tr::now,
lt_from,
fromLinkText(), // Link 1.
Ui::Text::WithEntities);
setServiceText(std::move(result));
}
} else if (out()) {
auto text = (data.is_voice()
? tr::lng_ttl_voice_sent
: data.is_round()
? tr::lng_ttl_round_sent
: tr::lng_message_empty)(tr::now, Ui::Text::WithEntities);
setServiceText({ std::move(text) });
}
} else {
setServiceText({
tr::lng_ttl_video_expired(tr::now, Ui::Text::WithEntities)
});
auto text = (data.is_video()
? tr::lng_ttl_video_expired
: data.is_voice()
? tr::lng_ttl_voice_expired
: data.is_round()
? tr::lng_ttl_round_expired
: tr::lng_message_empty)(tr::now, Ui::Text::WithEntities);
setServiceText({ std::move(text) });
}
}, [&](const MTPDmessageMediaStory &data) {
setServiceText(prepareStoryMentionText());

View file

@ -451,7 +451,7 @@ MediaCheckResult CheckMessageMedia(const MTPMessageMedia &media) {
}, [](const MTPDmessageMediaPhoto &data) {
const auto photo = data.vphoto();
if (data.vttl_seconds()) {
return Result::HasTimeToLive;
return Result::HasUnsupportedTimeToLive;
} else if (!photo) {
return Result::Empty;
}
@ -463,7 +463,11 @@ MediaCheckResult CheckMessageMedia(const MTPMessageMedia &media) {
}, [](const MTPDmessageMediaDocument &data) {
const auto document = data.vdocument();
if (data.vttl_seconds()) {
return Result::HasTimeToLive;
if (data.is_video()) {
return Result::HasUnsupportedTimeToLive;
} else if (!document) {
return Result::HasExpiredMediaTimeToLive;
}
} else if (!document) {
return Result::Empty;
}

View file

@ -43,7 +43,8 @@ enum class MediaCheckResult {
Good,
Unsupported,
Empty,
HasTimeToLive,
HasExpiredMediaTimeToLive,
HasUnsupportedTimeToLive,
HasStoryMention,
};
[[nodiscard]] MediaCheckResult CheckMessageMedia(

View file

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/audio/media_audio.h"
#include "media/player/media_player_instance.h"
#include "history/history_item_components.h"
#include "history/history_item_helpers.h" // PreparedServiceText.
#include "history/history.h"
#include "core/click_handler_types.h" // kDocumentFilenameTooltipProperty.
#include "history/view/history_view_element.h"
@ -47,6 +48,10 @@ namespace {
constexpr auto kAudioVoiceMsgUpdateView = crl::time(100);
[[nodiscard]] bool OncePlayable(not_null<HistoryItem*> item) {
return !item->out() && item->media()->ttlSeconds();
}
[[nodiscard]] QString CleanTagSymbols(const QString &value) {
auto result = QString();
const auto begin = value.begin(), end = value.end();
@ -195,7 +200,8 @@ Document::Document(
not_null<DocumentData*> document)
: File(parent, realParent)
, _data(document) {
if (_data->isVideoMessage()) {
const auto isRound = _data->isVideoMessage();
if (isRound) {
const auto &entry = _data->session().api().transcribes().entry(
realParent);
_transcribedRound = entry.shown;
@ -209,7 +215,49 @@ Document::Document(
_tooltipFilename.setTooltipText(named->name);
}
setDocumentLinks(_data, realParent);
if ((_data->isVoiceMessage() || isRound)
&& OncePlayable(_parent->data())) {
_parent->data()->removeFromSharedMediaIndex();
setDocumentLinks(_data, realParent, [=] {
_openl = nullptr;
auto lifetime = std::make_shared<rpl::lifetime>();
rpl::merge(
::Media::Player::instance()->updatedNotifier(
) | rpl::filter([=](::Media::Player::TrackState state) {
using State = ::Media::Player::State;
const auto badState = state.state == State::Stopped
|| state.state == State::StoppedAtEnd
|| state.state == State::StoppedAtError
|| state.state == State::StoppedAtStart;
return (state.id.contextId() != _realParent->fullId())
&& !badState;
}) | rpl::to_empty,
::Media::Player::instance()->tracksFinished(
) | rpl::filter([=](AudioMsgId::Type type) {
return (type == AudioMsgId::Type::Voice);
}) | rpl::to_empty
) | rpl::start_with_next([=]() mutable {
const auto item = _parent->data();
// Destroys this.
item->applyEditionToHistoryCleared();
item->updateServiceText(PreparedServiceText{
(isRound
? tr::lng_ttl_round_expired
: tr::lng_ttl_voice_expired)(
tr::now,
Ui::Text::WithEntities)
});
if (lifetime) {
base::take(lifetime)->destroy();
}
}, *lifetime);
return false;
});
} else {
setDocumentLinks(_data, realParent);
}
setStatusSize(Ui::FileStatusSizeReady);
@ -273,9 +321,9 @@ void Document::createComponents(bool caption) {
_realParent->fullId());
}
if (const auto voice = Get<HistoryDocumentVoice>()) {
voice->seekl = std::make_shared<VoiceSeekClickHandler>(
_data,
[](FullMsgId) {});
voice->seekl = !OncePlayable(_parent->data())
? std::make_shared<VoiceSeekClickHandler>(_data, [](FullMsgId) {})
: nullptr;
if (_transcribedRound) {
voice->round = std::make_unique<::Media::Player::RoundPainter>(
_realParent);

View file

@ -65,7 +65,8 @@ constexpr auto kMaxOriginalEntryLines = 8192;
parent,
*document,
skipPremiumEffect,
spoiler));
spoiler,
/*ttlSeconds = */0));
} else if (const auto photo = std::get_if<PhotoData*>(&item)) {
result.push_back(std::make_unique<Data::MediaPhoto>(
parent,

View file

@ -752,13 +752,20 @@ void MainWidget::searchMessages(const QString &query, Dialogs::Key inChat) {
void MainWidget::handleAudioUpdate(const Media::Player::TrackState &state) {
using State = Media::Player::State;
const auto document = state.id.audio();
const auto item = session().data().message(state.id.contextId());
if (!Media::Player::IsStoppedOrStopping(state.state)) {
createPlayer();
const auto ttlSeconds = item
&& !item->out()
&& item->media()
&& item->media()->ttlSeconds();
if (!ttlSeconds) {
createPlayer();
}
} else if (state.state == State::StoppedAtStart) {
Media::Player::instance()->stopAndClose();
}
if (const auto item = session().data().message(state.id.contextId())) {
if (item) {
session().data().requestItemRepaint(item);
}
if (document) {

View file

@ -503,6 +503,9 @@ bool Instance::moveInPlaylist(
}
const auto jumpByItem = [&](not_null<HistoryItem*> item) {
if (const auto media = item->media()) {
if (media->ttlSeconds()) {
return false;
}
if (const auto document = media->document()) {
if (autonext) {
_switchToNext.fire({