mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +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_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_sent" = "You sent a self-destructing video.";
|
||||||
"lng_ttl_video_expired" = "Video has expired";
|
"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_add_more_after_create" = "You will be able to add more members after you create the group.";
|
||||||
"lng_profile_camera_title" = "Capture yourself";
|
"lng_profile_camera_title" = "Capture yourself";
|
||||||
|
|
|
@ -555,6 +555,10 @@ bool Media::hasSpoiler() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
crl::time Media::ttlSeconds() const {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool Media::consumeMessageText(const TextWithEntities &text) {
|
bool Media::consumeMessageText(const TextWithEntities &text) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -849,12 +853,14 @@ MediaFile::MediaFile(
|
||||||
not_null<HistoryItem*> parent,
|
not_null<HistoryItem*> parent,
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
bool skipPremiumEffect,
|
bool skipPremiumEffect,
|
||||||
bool spoiler)
|
bool spoiler,
|
||||||
|
crl::time ttlSeconds)
|
||||||
: Media(parent)
|
: Media(parent)
|
||||||
, _document(document)
|
, _document(document)
|
||||||
, _emoji(document->sticker() ? document->sticker()->alt : QString())
|
, _emoji(document->sticker() ? document->sticker()->alt : QString())
|
||||||
, _skipPremiumEffect(skipPremiumEffect)
|
, _skipPremiumEffect(skipPremiumEffect)
|
||||||
, _spoiler(spoiler) {
|
, _spoiler(spoiler)
|
||||||
|
, _ttlSeconds(ttlSeconds) {
|
||||||
parent->history()->owner().registerDocumentItem(_document, parent);
|
parent->history()->owner().registerDocumentItem(_document, parent);
|
||||||
|
|
||||||
if (!_emoji.isEmpty()) {
|
if (!_emoji.isEmpty()) {
|
||||||
|
@ -882,7 +888,8 @@ std::unique_ptr<Media> MediaFile::clone(not_null<HistoryItem*> parent) {
|
||||||
parent,
|
parent,
|
||||||
_document,
|
_document,
|
||||||
!_document->session().premium(),
|
!_document->session().premium(),
|
||||||
_spoiler);
|
_spoiler,
|
||||||
|
_ttlSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
DocumentData *MediaFile::document() const {
|
DocumentData *MediaFile::document() const {
|
||||||
|
@ -1129,6 +1136,10 @@ bool MediaFile::hasSpoiler() const {
|
||||||
return _spoiler;
|
return _spoiler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
crl::time MediaFile::ttlSeconds() const {
|
||||||
|
return _ttlSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
bool MediaFile::updateInlineResultMedia(const MTPMessageMedia &media) {
|
bool MediaFile::updateInlineResultMedia(const MTPMessageMedia &media) {
|
||||||
if (media.type() != mtpc_messageMediaDocument) {
|
if (media.type() != mtpc_messageMediaDocument) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -174,6 +174,7 @@ public:
|
||||||
virtual bool dropForwardedInfo() const;
|
virtual bool dropForwardedInfo() const;
|
||||||
virtual bool forceForwardedInfo() const;
|
virtual bool forceForwardedInfo() const;
|
||||||
[[nodiscard]] virtual bool hasSpoiler() const;
|
[[nodiscard]] virtual bool hasSpoiler() const;
|
||||||
|
[[nodiscard]] virtual crl::time ttlSeconds() const;
|
||||||
|
|
||||||
[[nodiscard]] virtual bool consumeMessageText(
|
[[nodiscard]] virtual bool consumeMessageText(
|
||||||
const TextWithEntities &text);
|
const TextWithEntities &text);
|
||||||
|
@ -256,7 +257,8 @@ public:
|
||||||
not_null<HistoryItem*> parent,
|
not_null<HistoryItem*> parent,
|
||||||
not_null<DocumentData*> document,
|
not_null<DocumentData*> document,
|
||||||
bool skipPremiumEffect,
|
bool skipPremiumEffect,
|
||||||
bool spoiler);
|
bool spoiler,
|
||||||
|
crl::time ttlSeconds);
|
||||||
~MediaFile();
|
~MediaFile();
|
||||||
|
|
||||||
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
|
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
|
||||||
|
@ -278,6 +280,7 @@ public:
|
||||||
bool forwardedBecomesUnread() const override;
|
bool forwardedBecomesUnread() const override;
|
||||||
bool dropForwardedInfo() const override;
|
bool dropForwardedInfo() const override;
|
||||||
bool hasSpoiler() const override;
|
bool hasSpoiler() const override;
|
||||||
|
crl::time ttlSeconds() const override;
|
||||||
|
|
||||||
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
||||||
bool updateSentMedia(const MTPMessageMedia &media) override;
|
bool updateSentMedia(const MTPMessageMedia &media) override;
|
||||||
|
@ -292,6 +295,9 @@ private:
|
||||||
bool _skipPremiumEffect = false;
|
bool _skipPremiumEffect = false;
|
||||||
bool _spoiler = false;
|
bool _spoiler = false;
|
||||||
|
|
||||||
|
// Video (unsupported) / Voice / Round.
|
||||||
|
crl::time _ttlSeconds = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class MediaContact final : public Media {
|
class MediaContact final : public Media {
|
||||||
|
|
|
@ -242,7 +242,7 @@ std::unique_ptr<Data::Media> HistoryItem::CreateMedia(
|
||||||
});
|
});
|
||||||
}, [&](const MTPDmessageMediaDocument &media) -> Result {
|
}, [&](const MTPDmessageMediaDocument &media) -> Result {
|
||||||
const auto document = media.vdocument();
|
const auto document = media.vdocument();
|
||||||
if (media.vttl_seconds()) {
|
if (media.vttl_seconds() && media.is_video()) {
|
||||||
LOG(("App Error: "
|
LOG(("App Error: "
|
||||||
"Unexpected MTPMessageMediaDocument "
|
"Unexpected MTPMessageMediaDocument "
|
||||||
"with ttl_seconds in CreateMedia."));
|
"with ttl_seconds in CreateMedia."));
|
||||||
|
@ -258,7 +258,8 @@ std::unique_ptr<Data::Media> HistoryItem::CreateMedia(
|
||||||
item,
|
item,
|
||||||
item->history()->owner().processDocument(document),
|
item->history()->owner().processDocument(document),
|
||||||
media.is_nopremium(),
|
media.is_nopremium(),
|
||||||
media.is_spoiler());
|
media.is_spoiler(),
|
||||||
|
media.vttl_seconds().value_or_empty());
|
||||||
}, [](const MTPDdocumentEmpty &) -> Result {
|
}, [](const MTPDdocumentEmpty &) -> Result {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
});
|
});
|
||||||
|
@ -354,7 +355,8 @@ HistoryItem::HistoryItem(
|
||||||
setServiceText({
|
setServiceText({
|
||||||
tr::lng_message_empty(tr::now, Ui::Text::WithEntities)
|
tr::lng_message_empty(tr::now, Ui::Text::WithEntities)
|
||||||
});
|
});
|
||||||
} else if (checked == MediaCheckResult::HasTimeToLive) {
|
} else if ((checked == MediaCheckResult::HasUnsupportedTimeToLive)
|
||||||
|
|| (checked == MediaCheckResult::HasExpiredMediaTimeToLive)) {
|
||||||
createServiceFromMtp(data);
|
createServiceFromMtp(data);
|
||||||
applyTTL(data);
|
applyTTL(data);
|
||||||
} else if (checked == MediaCheckResult::HasStoryMention) {
|
} else if (checked == MediaCheckResult::HasStoryMention) {
|
||||||
|
@ -617,7 +619,8 @@ HistoryItem::HistoryItem(
|
||||||
this,
|
this,
|
||||||
document,
|
document,
|
||||||
skipPremiumEffect,
|
skipPremiumEffect,
|
||||||
spoiler);
|
spoiler,
|
||||||
|
/*ttlSeconds = */0);
|
||||||
setText(caption);
|
setText(caption);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1659,7 +1662,8 @@ void HistoryItem::setStoryFields(not_null<Data::Story*> story) {
|
||||||
this,
|
this,
|
||||||
document,
|
document,
|
||||||
/*skipPremiumEffect=*/false,
|
/*skipPremiumEffect=*/false,
|
||||||
spoiler);
|
spoiler,
|
||||||
|
/*ttlSeconds = */0);
|
||||||
}
|
}
|
||||||
setText(story->caption());
|
setText(story->caption());
|
||||||
}
|
}
|
||||||
|
@ -3605,25 +3609,43 @@ void HistoryItem::createServiceFromMtp(const MTPDmessage &message) {
|
||||||
const auto ttl = data.vttl_seconds();
|
const auto ttl = data.vttl_seconds();
|
||||||
Assert(ttl != nullptr);
|
Assert(ttl != nullptr);
|
||||||
|
|
||||||
setSelfDestruct(HistoryServiceSelfDestruct::Type::Video, *ttl);
|
if (data.is_video()) {
|
||||||
if (out()) {
|
setSelfDestruct(
|
||||||
setServiceText({
|
HistoryServiceSelfDestruct::Type::Video,
|
||||||
tr::lng_ttl_video_sent(tr::now, Ui::Text::WithEntities)
|
*ttl);
|
||||||
});
|
if (out()) {
|
||||||
} else {
|
setServiceText({
|
||||||
auto result = PreparedServiceText();
|
tr::lng_ttl_video_sent(
|
||||||
result.links.push_back(fromLink());
|
tr::now,
|
||||||
result.text = tr::lng_ttl_video_received(
|
Ui::Text::WithEntities)
|
||||||
tr::now,
|
});
|
||||||
lt_from,
|
} else {
|
||||||
fromLinkText(), // Link 1.
|
auto result = PreparedServiceText();
|
||||||
Ui::Text::WithEntities);
|
result.links.push_back(fromLink());
|
||||||
setServiceText(std::move(result));
|
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 {
|
} else {
|
||||||
setServiceText({
|
auto text = (data.is_video()
|
||||||
tr::lng_ttl_video_expired(tr::now, Ui::Text::WithEntities)
|
? 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) {
|
}, [&](const MTPDmessageMediaStory &data) {
|
||||||
setServiceText(prepareStoryMentionText());
|
setServiceText(prepareStoryMentionText());
|
||||||
|
|
|
@ -451,7 +451,7 @@ MediaCheckResult CheckMessageMedia(const MTPMessageMedia &media) {
|
||||||
}, [](const MTPDmessageMediaPhoto &data) {
|
}, [](const MTPDmessageMediaPhoto &data) {
|
||||||
const auto photo = data.vphoto();
|
const auto photo = data.vphoto();
|
||||||
if (data.vttl_seconds()) {
|
if (data.vttl_seconds()) {
|
||||||
return Result::HasTimeToLive;
|
return Result::HasUnsupportedTimeToLive;
|
||||||
} else if (!photo) {
|
} else if (!photo) {
|
||||||
return Result::Empty;
|
return Result::Empty;
|
||||||
}
|
}
|
||||||
|
@ -463,7 +463,11 @@ MediaCheckResult CheckMessageMedia(const MTPMessageMedia &media) {
|
||||||
}, [](const MTPDmessageMediaDocument &data) {
|
}, [](const MTPDmessageMediaDocument &data) {
|
||||||
const auto document = data.vdocument();
|
const auto document = data.vdocument();
|
||||||
if (data.vttl_seconds()) {
|
if (data.vttl_seconds()) {
|
||||||
return Result::HasTimeToLive;
|
if (data.is_video()) {
|
||||||
|
return Result::HasUnsupportedTimeToLive;
|
||||||
|
} else if (!document) {
|
||||||
|
return Result::HasExpiredMediaTimeToLive;
|
||||||
|
}
|
||||||
} else if (!document) {
|
} else if (!document) {
|
||||||
return Result::Empty;
|
return Result::Empty;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,8 @@ enum class MediaCheckResult {
|
||||||
Good,
|
Good,
|
||||||
Unsupported,
|
Unsupported,
|
||||||
Empty,
|
Empty,
|
||||||
HasTimeToLive,
|
HasExpiredMediaTimeToLive,
|
||||||
|
HasUnsupportedTimeToLive,
|
||||||
HasStoryMention,
|
HasStoryMention,
|
||||||
};
|
};
|
||||||
[[nodiscard]] MediaCheckResult CheckMessageMedia(
|
[[nodiscard]] MediaCheckResult CheckMessageMedia(
|
||||||
|
|
|
@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "media/audio/media_audio.h"
|
#include "media/audio/media_audio.h"
|
||||||
#include "media/player/media_player_instance.h"
|
#include "media/player/media_player_instance.h"
|
||||||
#include "history/history_item_components.h"
|
#include "history/history_item_components.h"
|
||||||
|
#include "history/history_item_helpers.h" // PreparedServiceText.
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "core/click_handler_types.h" // kDocumentFilenameTooltipProperty.
|
#include "core/click_handler_types.h" // kDocumentFilenameTooltipProperty.
|
||||||
#include "history/view/history_view_element.h"
|
#include "history/view/history_view_element.h"
|
||||||
|
@ -47,6 +48,10 @@ namespace {
|
||||||
|
|
||||||
constexpr auto kAudioVoiceMsgUpdateView = crl::time(100);
|
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) {
|
[[nodiscard]] QString CleanTagSymbols(const QString &value) {
|
||||||
auto result = QString();
|
auto result = QString();
|
||||||
const auto begin = value.begin(), end = value.end();
|
const auto begin = value.begin(), end = value.end();
|
||||||
|
@ -195,7 +200,8 @@ Document::Document(
|
||||||
not_null<DocumentData*> document)
|
not_null<DocumentData*> document)
|
||||||
: File(parent, realParent)
|
: File(parent, realParent)
|
||||||
, _data(document) {
|
, _data(document) {
|
||||||
if (_data->isVideoMessage()) {
|
const auto isRound = _data->isVideoMessage();
|
||||||
|
if (isRound) {
|
||||||
const auto &entry = _data->session().api().transcribes().entry(
|
const auto &entry = _data->session().api().transcribes().entry(
|
||||||
realParent);
|
realParent);
|
||||||
_transcribedRound = entry.shown;
|
_transcribedRound = entry.shown;
|
||||||
|
@ -209,7 +215,49 @@ Document::Document(
|
||||||
_tooltipFilename.setTooltipText(named->name);
|
_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);
|
setStatusSize(Ui::FileStatusSizeReady);
|
||||||
|
|
||||||
|
@ -273,9 +321,9 @@ void Document::createComponents(bool caption) {
|
||||||
_realParent->fullId());
|
_realParent->fullId());
|
||||||
}
|
}
|
||||||
if (const auto voice = Get<HistoryDocumentVoice>()) {
|
if (const auto voice = Get<HistoryDocumentVoice>()) {
|
||||||
voice->seekl = std::make_shared<VoiceSeekClickHandler>(
|
voice->seekl = !OncePlayable(_parent->data())
|
||||||
_data,
|
? std::make_shared<VoiceSeekClickHandler>(_data, [](FullMsgId) {})
|
||||||
[](FullMsgId) {});
|
: nullptr;
|
||||||
if (_transcribedRound) {
|
if (_transcribedRound) {
|
||||||
voice->round = std::make_unique<::Media::Player::RoundPainter>(
|
voice->round = std::make_unique<::Media::Player::RoundPainter>(
|
||||||
_realParent);
|
_realParent);
|
||||||
|
|
|
@ -65,7 +65,8 @@ constexpr auto kMaxOriginalEntryLines = 8192;
|
||||||
parent,
|
parent,
|
||||||
*document,
|
*document,
|
||||||
skipPremiumEffect,
|
skipPremiumEffect,
|
||||||
spoiler));
|
spoiler,
|
||||||
|
/*ttlSeconds = */0));
|
||||||
} else if (const auto photo = std::get_if<PhotoData*>(&item)) {
|
} else if (const auto photo = std::get_if<PhotoData*>(&item)) {
|
||||||
result.push_back(std::make_unique<Data::MediaPhoto>(
|
result.push_back(std::make_unique<Data::MediaPhoto>(
|
||||||
parent,
|
parent,
|
||||||
|
|
|
@ -752,13 +752,20 @@ void MainWidget::searchMessages(const QString &query, Dialogs::Key inChat) {
|
||||||
void MainWidget::handleAudioUpdate(const Media::Player::TrackState &state) {
|
void MainWidget::handleAudioUpdate(const Media::Player::TrackState &state) {
|
||||||
using State = Media::Player::State;
|
using State = Media::Player::State;
|
||||||
const auto document = state.id.audio();
|
const auto document = state.id.audio();
|
||||||
|
const auto item = session().data().message(state.id.contextId());
|
||||||
if (!Media::Player::IsStoppedOrStopping(state.state)) {
|
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) {
|
} else if (state.state == State::StoppedAtStart) {
|
||||||
Media::Player::instance()->stopAndClose();
|
Media::Player::instance()->stopAndClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const auto item = session().data().message(state.id.contextId())) {
|
if (item) {
|
||||||
session().data().requestItemRepaint(item);
|
session().data().requestItemRepaint(item);
|
||||||
}
|
}
|
||||||
if (document) {
|
if (document) {
|
||||||
|
|
|
@ -503,6 +503,9 @@ bool Instance::moveInPlaylist(
|
||||||
}
|
}
|
||||||
const auto jumpByItem = [&](not_null<HistoryItem*> item) {
|
const auto jumpByItem = [&](not_null<HistoryItem*> item) {
|
||||||
if (const auto media = item->media()) {
|
if (const auto media = item->media()) {
|
||||||
|
if (media->ttlSeconds()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (const auto document = media->document()) {
|
if (const auto document = media->document()) {
|
||||||
if (autonext) {
|
if (autonext) {
|
||||||
_switchToNext.fire({
|
_switchToNext.fire({
|
||||||
|
|
Loading…
Add table
Reference in a new issue