mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-19 07:37:11 +02:00
Added initial support for voice messages with TTL.
This commit is contained in:
parent
c2712b0104
commit
85286684e3
10 changed files with 144 additions and 37 deletions
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -43,7 +43,8 @@ enum class MediaCheckResult {
|
|||
Good,
|
||||
Unsupported,
|
||||
Empty,
|
||||
HasTimeToLive,
|
||||
HasExpiredMediaTimeToLive,
|
||||
HasUnsupportedTimeToLive,
|
||||
HasStoryMention,
|
||||
};
|
||||
[[nodiscard]] MediaCheckResult CheckMessageMedia(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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({
|
||||
|
|
Loading…
Add table
Reference in a new issue